﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web;
using System.Threading;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Web.Security;
using Heritage.Logging;
using System.Drawing.Imaging;
using Heritage.Models;
using OpenPOP.MIME;
using OpenPOP.POP3;

namespace Heritage.Codebox
{
    public class Utils
    {
        public static string GetServerVersion()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.Version).FirstOrDefault();
        }
        public static int GetRegistrationMode()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.RegistrationMode).FirstOrDefault();
        }
        public static bool IsAccessAllowed()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.EnteringMode).FirstOrDefault() == Constants.ACCESS_ALLOWED;
        }

        public static bool IsAuthenticated()
        {
            return Thread.CurrentPrincipal.Identity.IsAuthenticated;
        }
        public static bool IsUserValidated()
        {
            if (!IsAuthenticated()) return false;
            HeritageEntities ents = new HeritageEntities();
            return ents.Users.Where(r => r.Username == Thread.CurrentPrincipal.Identity.Name).FirstOrDefault().Validated;
        }
        public static string GetAdminEmail()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.AdminEmail).FirstOrDefault();
        }
        public static string GetServerEmail()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.ServerEmail).FirstOrDefault();
        }

        public static string GetServerAddress()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.ServerSettings.Select(r => r.ServerAddress).FirstOrDefault();
        }

        public static bool CheckCode(string code)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.RegistrationCodes.Where(r => r.RegisterCode == code && r.UsedBy == null).FirstOrDefault() != null;
        }

        public static Guid? GetReferrer(string code)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.RegistrationCodes.Where(r => r.RegisterCode == code).Select(t=>t.CreatedBy).FirstOrDefault();
        }

        public static bool CheckUserRegistered(string username)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Users.Where(r => r.Username == username).FirstOrDefault() != null;
        }

        public static bool CheckEmailRegistered(string email)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Users.Where(r => r.Email == email).FirstOrDefault() != null;
        }

        public static bool UseCode(string code, Guid who)
        {
            HeritageEntities ents = new HeritageEntities();
            RegistrationCodes codes = ents.RegistrationCodes.Where(r => r.RegisterCode == code && r.UsedBy == null).FirstOrDefault();
            if (codes == null) return false;
            if (!codes.IsPermanent)
            {
                codes.UsedBy = who;
                codes.UsedAt = DateTime.Now;
                ents.SaveChanges();
            }
            return true;
        }

        public static string HashString(string Value)
        {
            System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] data = System.Text.Encoding.ASCII.GetBytes(Value);
            data = x.ComputeHash(data);
            string ret = "";
            for (int i = 0; i < data.Length; i++)
                ret += data[i].ToString("x2").ToLower();
            return ret;
        }

        public static string GetServerURL(HttpRequestBase Request)
        {
            int cc = Request.Url.AbsoluteUri.IndexOf("/");
            cc = Request.Url.AbsoluteUri.IndexOf("/", cc+1);
            cc = Request.Url.AbsoluteUri.IndexOf("/", cc+1);
            if (cc == -1) return Request.Url.AbsoluteUri;
            return Request.Url.AbsoluteUri.Substring(0, cc);
        }

        public static void SendTemplate(int templateNum, string email, bool html, params string[] p)
        {
            HeritageEntities ents = new HeritageEntities();
            EmailTemplates tmpl = ents.EmailTemplates.Where(r => r.ID == templateNum).FirstOrDefault();
            SmtpClient client = new SmtpClient("localhost");
            MailMessage msg = new MailMessage(Utils.GetServerEmail(), email, string.Format(tmpl.Subject, p[0]), string.Format(tmpl.Body, p));
            msg.IsBodyHtml = html;
            msg.SubjectEncoding = Encoding.GetEncoding("koi8-r");
            msg.BodyEncoding = Encoding.GetEncoding("koi8-r");
            LogService.Debug(Utils.GetPrincipalName(), Constants.COMPONENT_SENDMAIL, string.Format("Sending mail message, template {0}, email {1}", templateNum, email));
            client.SendAsync(msg, null);
        }

        public static void SendJoinNotification(string email, params string[] p)
        {
            string notif = "Здравствуйте, {1}!\n\nВы записались в партию {2}.\nВаш ник: {3}\nВаш пол: {4}\nВаш почтовый пароль: {5}\n\nУдачной игры!\n\n----------------\nПочтовый робот сервера "+GetServerAddress();
            SmtpClient client = new SmtpClient("localhost");
            MailMessage msg = new MailMessage(Utils.GetServerEmail(), email, string.Format("Подтверждение записи в игру {0}", p[0]), string.Format(notif, p));
            msg.IsBodyHtml = false;
            msg.SubjectEncoding = Encoding.GetEncoding("koi8-r");
            msg.BodyEncoding = Encoding.GetEncoding("koi8-r");
            LogService.Debug(Utils.GetPrincipalName(), Constants.COMPONENT_SENDMAIL, string.Format("Sending join confirmation to {0}, game {1}", email, p[0]));
            client.SendAsync(msg, null);
        }

        public static void SendMail(string email, string subj, string body)
        {
            SmtpClient client = new SmtpClient("localhost");
            MailMessage msg = new MailMessage(Utils.GetServerEmail(), email, subj, body);
            msg.SubjectEncoding = Encoding.GetEncoding("koi8-r");
            msg.BodyEncoding = Encoding.GetEncoding("koi8-r");
            msg.IsBodyHtml = false;
            LogService.Debug(Utils.GetPrincipalName(), Constants.COMPONENT_SENDMAIL, string.Format("Sending email to {0}", email));
            client.SendAsync(msg, null);
        }

        public static void SendLeaveNotification(string email, params string[] p)
        {
            string notif = "Здравствуйте, {1}!\n\nВы выписались из партии {2}.\n\nЖдем вас в других играх!\n\n----------------\nПочтовый робот сервера " + GetServerAddress();
            SmtpClient client = new SmtpClient("localhost");
            MailMessage msg = new MailMessage(Utils.GetServerEmail(), email, string.Format("Подтверждение выхода из партии {0}", p[0]), string.Format(notif, p));
            msg.IsBodyHtml = false;
            msg.SubjectEncoding = Encoding.GetEncoding("koi8-r");
            msg.BodyEncoding = Encoding.GetEncoding("koi8-r");
            LogService.Debug(Utils.GetPrincipalName(), Constants.COMPONENT_SENDMAIL, string.Format("Sending leave confirmation email to {0}, game {1}", email, p[0]));
            client.SendAsync(msg, null);
        }

        public static string SignIn(string username, string password, HttpResponseBase response)
        {
            HeritageEntities ents = new HeritageEntities();
            Users user = ents.Users.Where(r => r.Username == username).FirstOrDefault();
            if (user == null) return null;
            if (user.Password != HashString(password.Trim())) return null;
            string userData = String.Empty;
            userData = userData + "Username" + user.Username;
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddDays(7), true, userData);
            string encTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie faCookie =new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
            response.Cookies.Add(faCookie);
            // And send the user where they were heading
            string redirectUrl ="/Home/Personal";
            return redirectUrl;
        }

        public static void SignOut()
        {
            FormsAuthentication.SignOut();
        }

        public static string ValidateUser(string code)
        {
            HeritageEntities ents = new HeritageEntities();
            Users user = ents.Users.Where(r => r.ValidationKey == code).FirstOrDefault();
            if (user == null) return null;
            user.Validated = true;
            ents.SaveChanges();
            ents.Dispose();
            return user.Username;
        }

        private static Users cachedUser = null;

        public static Users GetCurrentUser()
        {
            if (cachedUser == null || cachedUser.Username != Thread.CurrentPrincipal.Identity.Name)
            {
                HeritageEntities ents = new HeritageEntities();
                cachedUser = ents.Users.Where(r => r.Username == Thread.CurrentPrincipal.Identity.Name).FirstOrDefault();
            }
            return cachedUser;
        }

        public static Users GetUserByID(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            try
            {
                Guid guid = Guid.Parse(id);
                Users user = ents.Users.Where(r => r.ID == guid).FirstOrDefault();
                return user;
            }
            catch
            {
                Users user = GetCurrentUser();
                return user;
            }
        }

        public static PersonalData GetPersonalDataByID(Guid id)
        {
            HeritageEntities ents = new HeritageEntities();
            PersonalData data = ents.PersonalData.Where(r => r.UserID == id).FirstOrDefault();
            return data;
        }

        public static bool UserExists(string u)
        {
            HeritageEntities ents = new HeritageEntities();
            Users user = ents.Users.Where(r => r.Username == u).FirstOrDefault();
            return user != null;
        }

        public static string GetGameNicks(Guid guid)
        {
            return "";
        }

        public static string GetFirstGameName(Guid guid)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Games.Join(ents.GameUserData.Where(r => r.UserID == guid || r.UserID2 == guid), u => u.GameName, v => v.GameName, (u, v) => u).Where(r => r.GameState == Constants.GAME_STATE_FINISHED).OrderBy(t=>t.GameInitiated).Select(y=>y.GameName).FirstOrDefault();
        }

        public static int GetPlayedGames(Guid guid)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Games.Join(ents.GameUserData.Where(r => r.UserID == guid || r.UserID2 == guid), u => u.GameName, v => v.GameName, (u, v) => u).Where(r => r.GameState == Constants.GAME_STATE_FINISHED).Count();
        }

        public static void UpdateLastVisit()
        {
            HeritageEntities ents = new HeritageEntities();
            Users user = ents.Users.Where(r => r.Username == Thread.CurrentPrincipal.Identity.Name).FirstOrDefault();
            if (user != null)
            {
                user.LastVisitDate = DateTime.Now;
                ents.SaveChanges();
            }
        }

        public static DateTime? GetLastVisit(Guid id)
        {
            HeritageEntities ents = new HeritageEntities();
            Users user = ents.Users.Where(r => r.ID == id).FirstOrDefault();
            if (user != null)
            {
                return user.LastVisitDate;
            }
            return null;
        }

        public static IEnumerable<Games> GetActiveGames() 
        {
            HeritageEntities ents = new HeritageEntities();
            List<Games> res = new List<Games>();
            foreach (Games g in ents.Games.Where(r => r.GameState != Constants.GAME_STATE_AVAILABLE))
            {
                if (g.GameState == Constants.GAME_STATE_IN_PROGRESS) res.Add(g);
                if (IsFinishedLessThanArchiveTime(g.GameFinished)) res.Add(g);
            }
            return res;
        }

        private static bool IsFinishedLessThanArchiveTime(DateTime? dt)
        {
            if (dt == null) return false;
            if ((DateTime.Now - (DateTime)dt).TotalDays < Constants.GAME_GOES_TO_ARCHIVE_DAYS) return true;
            return false;
        }

        public static IEnumerable<Games> GetAvailableGames()
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Games.Where(r => r.GameState == Constants.GAME_STATE_AVAILABLE).ToList();
        }

        public static string GetGameSchedule(Games gm)
        {
            string s = gm.GameSchedule;
            StringBuilder sb = new StringBuilder();
            sb.Append(GetGameSchedule(gm.GameSchedule));
            if (gm.GameState == Constants.GAME_STATE_IN_PROGRESS)
            {
                DateTime dt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, GetScheduledHour(s), 0, 0);
                while (true)
                {
                    if (DateTime.Now > dt)
                    {
                        dt = dt.AddDays(1);
                        continue;
                    }
                    if (((DateTime)gm.GameStarted).AddDays(Constants.GAME_FIRST_DAY_GAP) > dt)
                    {
                        dt = dt.AddDays(1);
                        continue;
                    }
                    string res = null;
                    if (dt.DayOfWeek == DayOfWeek.Monday && s.IndexOf("A") >= 0) res = "в понедельник";
                    if (dt.DayOfWeek == DayOfWeek.Tuesday && s.IndexOf("B") >= 0) res = "во вторник";
                    if (dt.DayOfWeek == DayOfWeek.Wednesday && s.IndexOf("C") >= 0) res = "в среду";
                    if (dt.DayOfWeek == DayOfWeek.Thursday && s.IndexOf("D") >= 0) res = "в четверг";
                    if (dt.DayOfWeek == DayOfWeek.Friday && s.IndexOf("E") >= 0) res = "в пятницу";
                    if (dt.DayOfWeek == DayOfWeek.Saturday && s.IndexOf("F") >= 0) res = "в субботу";
                    if (dt.DayOfWeek == DayOfWeek.Sunday && s.IndexOf("G") >= 0) res = "в воскресенье";
                    if (res != null)
                    {
                        sb.Append(string.Format("<br><span style=\"font-weight: normal;\"><i>следующий ход будет {0}</i></span>", res));
                        break;
                    }
                    else
                    {
                        dt = dt.AddDays(1);
                    }
                }
            }
            return sb.ToString();
        }

        public static string GetGameSchedule(string s)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in s)
            {
                if (c == 'A') sb.Append("пн, ");
                if (c == 'B') sb.Append("вт, ");
                if (c == 'C') sb.Append("ср, ");
                if (c == 'D') sb.Append("чт, ");
                if (c == 'E') sb.Append("пт, ");
                if (c == 'F') sb.Append("сб, ");
                if (c == 'G') sb.Append("вс, ");
                if (Char.IsDigit(c))
                {
                    sb.Append("<br>");
                    sb.Append(GetScheduledHour(s) + ":00 МСК");
                    break;
                }
            }
            return sb.ToString();
        }

        public static string GetGameConditions(Games g)
        {
            StringBuilder sb = new StringBuilder();
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_NONE) sb.Append("нет комиссара, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_CHECKING) sb.Append("комиссар, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_KILLING) sb.Append("стреляющий комиссар, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_REANIMATING) sb.Append("реанимирующий комиссар, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_NONE) sb.Append("нет мафии, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_WEAK) sb.Append("слабая мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_NORMAL) sb.Append("мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_STRONG) sb.Append("сильная мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_VERYSTRONG) sb.Append("очень сильная мафия, ");
            if (g.Advocat != 0) sb.Append("адвокат, ");
            if (g.Sheriff != 0) sb.Append("шериф, ");
            if (g.Girl != 0) sb.Append("девочка, ");
            if (g.Reanimator != 0) sb.Append("реаниматор, ");
            if (g.Thief != 0) sb.Append("вор, ");
            if (g.Maniac != 0) sb.Append("маньяк, ");
            int i = g.Maniac - 1;
            while (i-- > 0) sb.Append("еще маньяк, ");
            if (g.Private != 0) sb.Append("без приватов, ");
            if (g.PublicVote != 0) sb.Append("тайное голосование, ");

            return sb.ToString().Substring(0, sb.Length - 2);
        }

        public static string GetGameConditions(GameDescriptions g)
        {
            StringBuilder sb = new StringBuilder();
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_NONE) sb.Append("нет комиссара, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_CHECKING) sb.Append("комиссар, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_KILLING) sb.Append("стреляющий комиссар, ");
            if (g.Komissar == Constants.GAMEDESC_KOMISSAR_REANIMATING) sb.Append("реанимирующий комиссар, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_NONE) sb.Append("нет мафии, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_WEAK) sb.Append("слабая мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_NORMAL) sb.Append("мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_STRONG) sb.Append("сильная мафия, ");
            if (g.Mafia == Constants.GAMEDESC_MAFIA_VERYSTRONG) sb.Append("очень сильная мафия, ");
            if (g.Advocat != 0) sb.Append("адвокат, ");
            if (g.Sheriff != 0) sb.Append("шериф, ");
            if (g.Girl != 0) sb.Append("девочка, ");
            if (g.Reanimator != 0) sb.Append("реаниматор, ");
            if (g.Thief != 0) sb.Append("вор, ");
            if (g.Maniac != 0) sb.Append("маньяк, ");
            int i = g.Maniac - 1;
            while (i-- > 0) sb.Append("еще маньяк, ");
            if (g.Private != 0) sb.Append("без приватов, ");
            if (g.PublicVote != 0) sb.Append("тайное голосование, ");

            return sb.ToString().Substring(0, sb.Length - 2);
        }

        public static PersonalData GetPersonalData()
        {
            Users user = GetCurrentUser();
            HeritageEntities ents = new HeritageEntities();
            PersonalData data = ents.PersonalData.Where(r => r.UserID == user.ID).FirstOrDefault();
            return data;
        }

        public static string GetLinks(Games g)
        {
            StringBuilder sb = new StringBuilder();
            Users user = GetCurrentUser();
            PersonalData data = GetPersonalData();
            HeritageEntities ents = new HeritageEntities();
            GameUserData guda =
                ents.GameUserData.Where(r => (r.UserID == user.ID || r.UserID2 == user.ID) && r.GameName == g.GameName).
                    FirstOrDefault();
            if (guda == null)
            {
                if ((g.RatingForEnter <= data.GameRating && g.Players > Utils.GetPlayersCount(g.GameName)) || IsModeratorAndAbove())
                {
                    sb.Append("<a href=\"/Game/Join/"+g.GameName+"\">записаться</a><br>");
                }
                else
                {
                    if (g.RatingForEnter > data.GameRating)
                    {
                        sb.Append("рейтинг мал<br>");
                    }
                    else if (g.Players <= Utils.GetPlayersCount(g.GameName))
                    {
                        sb.Append("запись закончена<br>");
                    }

                }
            }
            else if (g.GameState == Constants.GAME_STATE_AVAILABLE)
            {
                if (guda.UserID == user.ID)
                {

                    if (guda.UserID2 == null)
                    {
                        sb.Append("<a href=\"/Game/Leave/" + g.GameName +
                              "\" onclick=\"jConfirm('Вы уверены в том, что хотите покинуть партию <b>" + g.GameName +
                              "</b>?<br>Интереснейшие баталии пройдут без вас %).', 'Не передумаете, мм?', function(r) { if (r) {window.location='/Game/Leave/" +
                              g.GameName + "'}}); return false;\">выписаться</a><br>");
                        sb.Append("<a href=\"/Game/Invite/" + g.GameName + "\">пригласить в пару</a><br>");
                    }
                    else
                    {
                        sb.Append("<a href=\"/Game/LeavePair/" + g.GameName +
                                  "\" onclick=\"jConfirm('Вы уверены в том, что хотите покинуть парy в партии <b>" + g.GameName +
                                  "</b>?<br>Интереснейшие баталии пройдут без вас %).', 'Не передумаете, мм?', function(r) { if (r) {window.location='/Game/LeavePair/" +
                                  g.GameName + "'}}); return false;\">покинуть пару</a><br>");                        
                    }
                }
                if (guda.UserID2 == user.ID)
                {
                    sb.Append("<a href=\"/Game/LeavePair/" + g.GameName +
                              "\" onclick=\"jConfirm('Вы уверены в том, что хотите покинуть парy в партии <b>" + g.GameName +
                              "</b>?<br>Интереснейшие баталии пройдут без вас %).', 'Не передумаете, мм?', function(r) { if (r) {window.location='/Game/LeavePair/" +
                              g.GameName + "'}}); return false;\">покинуть пару</a><br>");
                }
            }
            if (IsModeratorAndAbove() || g.GameMayor == user.ID)
            {
                if (g.GameState == Constants.GAME_STATE_AVAILABLE || g.GameState == Constants.GAME_STATE_STOPPED)
                {
                    sb.Append("<a href=\"/GameMaster/EditGame/" + g.GameName + "\">редактировать</a><br>");
                    sb.Append("<a href=\"/GameMaster/StartGame/" + g.GameName + "\" onclick=\"jConfirm('Вы уверены в том, что хотите стартовать партию <b>" + g.GameName + "</b>?', 'Начнем мордобой?', function(r) { if (r) {window.location='/GameMaster/StartGame/" + g.GameName + "'}}); return false;\">стартовать</a><br>");
                    sb.Append("<a href=\"/GameMaster/DeleteGame/" + g.GameName + "\" onclick=\"jConfirm('Вы уверены в том, что хотите удалить партию <b>" + g.GameName + "</b>?', 'Серьезно?', function(r) { if (r) {window.location='/GameMaster/DeleteGame/" + g.GameName + "'}}); return false;\">удалить</a><br>");
                }
                if (g.GameState == Constants.GAME_STATE_IN_PROGRESS)
                {
                    sb.Append("<a href=\"/GameMaster/StopGame/" + g.GameName + "\" onclick=\"jConfirm('Вы уверены в том, что хотите остановить партию <b>" + g.GameName + "</b>?', 'Серьезно?', function(r) { if (r) {window.location='/GameMaster/StopGame/" + g.GameName + "'}}); return false;\">остановить</a><br>");
                    sb.Append("<a href=\"/GameMaster/GameTurn/" + g.GameName + "\" onclick=\"jConfirm('Вы уверены в том, что хотите сделать ход в партии <b>" + g.GameName + "</b>?', 'Серьезно?', function(r) { if (r) {window.location='/GameMaster/GameTurn/" + g.GameName + "'}}); return false;\">сделать ход</a><br>");
                }
                if (g.GameState == Constants.GAME_STATE_STOPPED)
                {
                    sb.Append("<a href=\"/GameMaster/Restart/" + g.GameName + "\" onclick=\"jConfirm('Вы уверены в том, что хотите рестартовать партию <b>" + g.GameName + "</b>?', 'Серьезно?', function(r) { if (r) {window.location='/GameMaster/Restart/" + g.GameName + "'}}); return false;\">рестартовать</a><br>");
                }
            }
            return sb.ToString();
        }

        public static bool IsSpecModeOn() 
        {
            return GetCurrentUser().SpecialMode == 1;
        }


        public static bool IsModeratorAndAbove()
        {
            return IsModeratorAndAbove(IsSpecModeOn());
        }

        public static bool IsGameMasterAndAbove()
        {
            return IsGameMasterAndAbove(IsSpecModeOn());
        }

        public static bool IsAdmin()
        {
            return IsAdmin(IsSpecModeOn());
        }

        private static bool IsModeratorAndAbove(bool useMode)
        {
            if (!useMode) return false;
            Users user = GetCurrentUser();
            return user.UserRole == Constants.USER_ROLE_MODERATOR || user.UserRole == Constants.USER_ROLE_GAMEMASTER || user.UserRole == Constants.USER_ROLE_ADMIN;
        }

        private static bool IsGameMasterAndAbove(bool useMode)
        {
            if (!useMode) return false;
            Users user = GetCurrentUser();
            return user.UserRole == Constants.USER_ROLE_GAMEMASTER || user.UserRole == Constants.USER_ROLE_ADMIN;
        }

        private static bool IsAdmin(bool useMode)
        {
            if (!useMode) return false;
            Users user = GetCurrentUser();
            return user.UserRole == Constants.USER_ROLE_ADMIN;
        }

        public static bool UserInGame(Guid guid, string id)
        {
            HeritageEntities ents = new HeritageEntities();
            if (ents.GameUserData.Where(r => (r.UserID == guid || r.UserID2 == guid) && r.GameName == id).FirstOrDefault() != null)
            {
                return true;
            }
            return false;
        }

        public static bool UserHasRating(Guid guid, string id)
        {
            HeritageEntities ents = new HeritageEntities();
            PersonalData data = GetPersonalData();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (data.GameRating >= g.RatingForEnter || IsModeratorAndAbove())
            {
                return true;
            }
            return false;
        }
        public static bool IsGameExists(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (g != null)
            {
                return true;
            }
            return false;
        }
        public static Games GetGame(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            return g;
        }
        public static bool IsGameAvailable(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (g.GameState == Constants.GAME_STATE_AVAILABLE)
            {
                return true;
            }
            return false;
        }
        public static bool IsGameHasRoom(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (g.Players > ents.GameUserData.Where(r=>r.GameName == id).Count())
            {
                return true;
            }
            return false;
        }

        public static bool MaskExists(string mask, string game)
        {
            HeritageEntities ents = new HeritageEntities();
            if (ents.GameUserData.Where(r => r.UserMask == mask && r.GameName == game).FirstOrDefault() != null)
            {
                return true;
            }
            return false;
        }

        public static int GetPlayersCount(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game).Count();
        }

        public static bool LeaveGame(string game)
        {
            Users user = GetCurrentUser();
            HeritageEntities ents = new HeritageEntities();
            GameUserData g = ents.GameUserData.Where(r => (r.UserID == user.ID) && r.GameName == game).FirstOrDefault();
            if (g != null)
            {
                ents.GameUserData.DeleteObject(g);
                ents.SaveChanges();
                SendLeaveNotification(user.Email, game, user.Username, game);
                return true;
            }
            return false;
        }

        public static bool LeavePair(string game)
        {
            Users user = GetCurrentUser();
            HeritageEntities ents = new HeritageEntities();
            GameUserData g = ents.GameUserData.Where(r => (r.UserID == user.ID || r.UserID2 == user.ID) && r.GameName == game).FirstOrDefault();
            if (g != null)
            {
                if (g.UserID2 == user.ID)
                {
                    g.UserID2 = null;
                    g.GamePassword2 = null;
                    g.UserEmail2 = null;
                    ents.SaveChanges();
                    SendLeaveNotification(user.Email, game, user.Username, game);
                    return true;
                }
                if (g.UserID == user.ID)
                {
                    if (g.UserID2 == null)
                    {
                        ents.GameUserData.DeleteObject(g);
                        ents.SaveChanges();
                        SendLeaveNotification(user.Email, game, user.Username, game);
                        return true;
                    }
                    g.UserID = g.UserID2.Value;
                    g.GamePassword = g.GamePassword2;
                    g.UserEmail = g.UserEmail2;
                    g.UserID2 = null;
                    g.GamePassword2 = null;
                    g.UserEmail2 = null;
                    ents.SaveChanges();
                    SendLeaveNotification(user.Email, game, user.Username, game);
                    return true;
                }
            }
            return false;
        }

        public static List<Games> GetMyActiveGames()
        {
            Users user = GetCurrentUser();
            HeritageEntities ents = new HeritageEntities();
            List<GameUserData> games = ents.GameUserData.Where(r => r.UserID == user.ID || r.UserID2 == user.ID).ToList();
            List<Games> gam = new List<Games>();
            foreach (GameUserData gud in games)
            {
                Games gg = ents.Games.Where(r => r.GameName == gud.GameName).FirstOrDefault();
                if (gg.GameState == Constants.GAME_STATE_AVAILABLE || gg.GameState == Constants.GAME_STATE_IN_PROGRESS)
                {
                    gam.Add(gg);
                }
                else
                {
                    if (gg.GameFinished != null && (DateTime.Now - ((DateTime)gg.GameFinished)).TotalDays < 3)
                    {
                        gam.Add(gg);
                    }
                }
            }
            return gam;
        }

        public static Users GetPairedUser(string gameID, Guid userID) 
        {
            HeritageEntities ents = new HeritageEntities();
            Users us = ents.Users.Join(ents.GameUserData.Where(r => r.GameName == gameID && r.UserID == userID), u=>u.ID, v=>v.UserID2, (u,v)=>u).FirstOrDefault();
            return us;
        }

        public static IList<Users> GetGameUsers(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Users.Join(ents.GameUserData.Where(h=>h.GameName == game).OrderBy(t=>t.UserMask), u => u.ID, g => g.UserID, (u, g) => u).ToList();            
        }

        public static IList<GameUserData> GetAllGameUsersData(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game).OrderBy(t=>t.UserMask).ToList();
        }

        public static IList<GameUserData> GetAllAliveGameUsersData(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserState == Constants.USERSTATE_ALIVE).OrderBy(t=>t.UserMask).ToList();
        }

        public static string GetUserMask(Guid guid, string id)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == id && (r.UserID == guid || r.UserID2 == guid)).Select(t => t.UserMask).FirstOrDefault();
        }

        public static double GetUserServerRating(Guid guid)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.PersonalData.Where(r => r.UserID == guid).Select(t => t.GameRating).FirstOrDefault();
        }

        public static int GetScheduledHour(string schedule) 
        {
            try
            {
                return int.Parse(schedule.Substring(schedule.Length - 2));
            }
            catch
            {
                return int.Parse(schedule.Substring(schedule.Length - 1));
            }
        }

        public static object CreateHourDropDown(string sch)
        {
            StringBuilder sb = new StringBuilder();
            int sh = GetScheduledHour(sch);
            sb.Append("<select name=\"hour\" class=\"smallinput\" style=\"height:20px; width: 100px;\">");
            for (int i = 0; i < 24; i++)
            {
                sb.Append(string.Format("<option value=\"{0}\"{1}>{2}</option>", i, (i == sh? " selected" : ""), i+":00 МСК"));
            }
            sb.Append("</select>");
            return sb.ToString();
        }

        public static string SpecialModeLinks()
        {
            string res = "";
            if (!IsModeratorAndAbove(true)) return "";
            if (IsAdmin(true))
            {
                res = "администратора";
            }
            else if (IsGameMasterAndAbove(true))
            {
                res = "гейм-мастера";
            }
            else if (IsModeratorAndAbove(true))
            {
                res = "модератора";
            }
            string current = "включен";
            string action = "Выключить";
            if (!IsSpecModeOn())
            {
                current = "выключен";
                action = "Включить";
            }
            return " | Режим " + res + " <b>" + current + "</b>. <a href=\"/Home/SwitchSpecMode\">" + action + "</a>";
        }

        public static bool CheckLettersOnly(string p)
        {
            foreach (char c in p)
            {
                if (!char.IsLetter(c) && c!='-' && c!='_') return false;
            }
            return true;
        }

        public static int GetNextValidGameNumber(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            int max = 1;
            foreach (string name in ents.Games.Where(r => r.GameName.StartsWith(game)).Select(t => t.GameName).ToList())
            {
                int num = int.Parse(name.Substring(game.Length));
                if (num >= max) max = num+1;
            }
            return max;
        }

        public static void SendGameDeleteNotification(string email, params string[] p)
        {
            string notif = "Здравствуйте, {1}!\n\nПартия {0}, в которую вы были записаны, удалена модератором.\n\nПриносим свои извинения и ждем вас в других играх!\n\n----------------\nПочтовый робот сервера " + GetServerAddress();
            SmtpClient client = new SmtpClient("localhost");
            MailMessage msg = new MailMessage(Utils.GetServerEmail(), email, string.Format("Партия {0} удалена с сервера", p[0]), string.Format(notif, p));
            msg.IsBodyHtml = false;
            LogService.Debug(Utils.GetPrincipalName(), Constants.COMPONENT_SENDMAIL, string.Format("Sending game {1} remove notification to {0}", email, p[0]));
            client.SendAsync(msg, null);
        }

        public static bool ImOwner(string party)
        {
            HeritageEntities ents = new HeritageEntities();
            return (ents.Games.Where(r => r.GameName == party && r.GameMayor == GetCurrentUser().ID).Count() > 0);
        }

        public static void StartOrContinueGame(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (g.GameState == Constants.GAME_STATE_STOPPED)
            {
                g.GameFinished = null;
                g.GameState = Constants.GAME_STATE_IN_PROGRESS;
                g.GameResult = 0;
                GameMessages gm = GameMessages.CreateGameMessages(1, id, DateTime.Now, "Вниманию всех участников!\n\nИгра была вновь запущена модератором или мэром.\nВсе статусы и состояния игроков остались такими, какими они были на момент остановки.\n\nЭто автоматическое сообщение, отвечать на него не нужно.", false, Constants.ADRESAT_GAMEMASTER, Constants.ADRESAT_EVERYBODY, false, Guid.NewGuid(), g.GameTurn, 0);
                ents.GameMessages.AddObject(gm);
                GameSchedule gs = GameSchedule.FromGameString(g.GameSchedule);
                gs.CalculateNextTurnTime(true);
                g.GameSchedule = gs.ToGameString();
                ents.SaveChanges();
            }
            else if (g.GameState == Constants.GAME_STATE_AVAILABLE)
            {
                StartGame(id);
            }
        }


        public static void StartGame(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            List<GameUserData> list = ents.GameUserData.Where(r => r.GameName == id).ToList();
            int mafs = (int)((double)g.Players / 4.5);
            if (g.Mafia == Constants.GAMEDESC_MAFIA_NONE) mafs = 0;
            if (g.Mafia == Constants.GAMEDESC_MAFIA_WEAK) mafs = mafs - 1;
            if (g.Mafia == Constants.GAMEDESC_MAFIA_STRONG) mafs = mafs + 1;
            if (g.Mafia == Constants.GAMEDESC_MAFIA_VERYSTRONG) mafs = mafs + 2;
            if (g.Thief > 0) mafs--;
            if (g.Advocat > 0) mafs--;
            Random rand = new Random();
            for (int i = 0; i < mafs; i++)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_MAFIA;
                if (list.Count == 0) break;
            }
            if (g.Advocat > 0 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_ADVOCAT;
            }
            if (g.Thief > 0 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_THIEF;
            }
            if (g.Komissar == 1 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_KOMISSAR_CHECKING;
            }
            if (g.Komissar == 2 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_KOMISSAR_KILLING;
            }
            if (g.Komissar == 3 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_KOMISSAR_REANIMATING;
            }
            if (g.Girl == 1 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_GIRL;
            }
            if (g.Reanimator == 1 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_REANIMATOR;
            }
            if (g.Sheriff == 1 && list.Count > 0)
            {
                int random = rand.Next(list.Count);
                GameUserData selected = list.ElementAt(random);
                list.Remove(selected);
                selected.UserRole = Constants.STATUS_SHERIFF;
            }
            if (g.Maniac > 0 && list.Count > 0)
            {
                for (int i = 0; i < g.Maniac; i++)
                {
                    int random = rand.Next(list.Count);
                    GameUserData selected = list.ElementAt(random);
                    list.Remove(selected);
                    selected.UserRole = Constants.STATUS_MANIAC;
                    if (list.Count == 0) break;
                }
            }
            foreach (GameUserData sel in list)
            {
                sel.UserRole = Constants.STATUS_CITIZEN;
                if (list.Count == 0) break;
            }
            g.GameState = Constants.GAME_STATE_IN_PROGRESS;
            g.GameStarted = DateTime.Now;
            g.GameFinished = null;
            g.GameResult = 0;

            foreach (GameUserData gud in ents.GameUserData.Where(r => r.GameName == id))
            {
                gud.UserVote = gud.UserID;
                gud.UserSpecVote = gud.UserID;
                gud.UserState = Constants.USERSTATE_ALIVE;
                gud.HarakiriMode = false;
                gud.UserResult = Constants.USERRESULT_NOT_FINISHED;
            }
            GameSchedule gs = GameSchedule.FromGameString(g.GameSchedule);
            gs.CalculateNextTurnTime(true);
            g.GameSchedule = gs.ToGameString();
            ents.SaveChanges();
            //MassSendNewspaper(id);
            GenerateMorningNewspaper(id);
            GenerateMayorWelcomeWord(g);
        }

        public static void GenerateMayorWelcomeWord(Games g) 
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Поздравляю с началом партии!<br><br>");
            if (g.GameSchedule != "MAYOR") 
            {
                sb.Append(string.Format("Информация о расписании ходов<br>{0}<br>", GameSchedule.FromGameString(g.GameSchedule).Description(true)));
            }
            else 
            {
                sb.Append("Ходы делает мэр города<br>");
            }
            sb.Append("<br>Статусы:<br>");
            IList<GameUserData> data = GetAllGameUsersData(g.GameName);
            int cnt = data.Where(r => r.UserRole == Constants.STATUS_MAFIA).Count();
            if (cnt > 0) sb.Append(string.Format("Мафиози: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_ADVOCAT).Count();
            if (cnt > 0) sb.Append(string.Format("Адвокат: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_THIEF).Count();
            if (cnt > 0) sb.Append(string.Format("Вор: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_MANIAC).Count();
            if (cnt > 0) sb.Append(string.Format("Маньяк: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_KOMISSAR_CHECKING).Count();
            if (cnt > 0) sb.Append(string.Format("Комиссар: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_KOMISSAR_KILLING).Count();
            if (cnt > 0) sb.Append(string.Format("Стреляющий комиссар: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_KOMISSAR_REANIMATING).Count();
            if (cnt > 0) sb.Append(string.Format("Реанимирующий комиссар: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_GIRL).Count();
            if (cnt > 0) sb.Append(string.Format("Девочка: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_SHERIFF).Count();
            if (cnt > 0) sb.Append(string.Format("Шериф: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_REANIMATOR).Count();
            if (cnt > 0) sb.Append(string.Format("Реаниматор: {0}<br>", cnt));
            cnt = data.Where(r => r.UserRole == Constants.STATUS_CITIZEN).Count();
            if (cnt > 0) sb.Append(string.Format("Честный житель: {0}<br>", cnt));
            sb.Append("<br>Удачной игры!");

            HeritageEntities ents = new HeritageEntities();
            GameMessages gm = GameMessages.CreateGameMessages(1, g.GameName, DateTime.Now, sb.ToString(), false, Constants.ADRESAT_MAYOR, Constants.ADRESAT_EVERYBODY, false, Guid.NewGuid(), 0, 0);
            ents.GameMessages.AddObject(gm);
            ents.SaveChanges();
        }

        public static string GetGameResultAsString(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == game).FirstOrDefault();
            if (g.GameResult == Constants.GAMERESULT_CITIZENS_WON) return "Победили честные жители.";
            if (g.GameResult == Constants.GAMERESULT_MAFIA_WON) return "Победила мафия.";
            if (g.GameResult == Constants.GAMERESULT_MANIAC_WON) return "Победил маньяк";
            if (g.GameResult == Constants.GAMERESULT_DRAW) return "Игра закончилась ничьей.";
            return "";
        }

        private static void GenerateMorningNewspaper(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games game = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            List<GameUserData> glist = ents.GameUserData.Where(r => r.GameName == id).ToList();
            foreach (GameUserData g in glist)
            {
                string s = GetNewspaper(game, g, true, true);
                GameMessages gm = GameMessages.CreateGameMessages(1, game.GameName, DateTime.Now, s, false, Constants.ADRESAT_GAZETA_MORNING, g.UserID, false, Guid.NewGuid(), game.GameTurn, 0);
                ents.GameMessages.AddObject(gm);
            }
            ents.SaveChanges();
        }

        public static string GetUserRoleString(GameUserData myData, GameUserData userData)
        {
            if (userData.UserRole == Constants.STATUS_CITIZEN)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "честный житель";
            }
            if (userData.UserRole == Constants.STATUS_KOMISSAR_CHECKING)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "комиссар";
            }
            if (userData.UserRole == Constants.STATUS_KOMISSAR_KILLING)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "стрелком";
            }
            if (userData.UserRole == Constants.STATUS_KOMISSAR_REANIMATING)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "ренком";
            }
            if (userData.UserRole == Constants.STATUS_GIRL)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "девочка";
            }
            if (userData.UserRole == Constants.STATUS_MANIAC)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "маньяк";
            }
            if (userData.UserRole == Constants.STATUS_REANIMATOR)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "реаниматор";
            }
            if (userData.UserRole == Constants.STATUS_SHERIFF)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE) return "шериф";
            }
            if (userData.UserRole == Constants.STATUS_THIEF)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE || myData.UserRole == Constants.STATUS_MAFIA || myData.UserRole == Constants.STATUS_ADVOCAT) return "вор";
            }
            if (userData.UserRole == Constants.STATUS_ADVOCAT)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE || myData.UserRole == Constants.STATUS_MAFIA || myData.UserRole == Constants.STATUS_THIEF) return "адвокат";
            }
            if (userData.UserRole == Constants.STATUS_MAFIA)
            {
                if (userData.UserID == myData.UserID || userData.UserState != Constants.USERSTATE_ALIVE || myData.UserRole == Constants.STATUS_THIEF || myData.UserRole == Constants.STATUS_ADVOCAT || myData.UserRole == Constants.STATUS_MAFIA) return "мафиози";
            }
            return "";
        }

        public static string GetSimpleUserRoleString(int role)
        {
            if (role == Constants.STATUS_CITIZEN) return "честный житель";
            if (role == Constants.STATUS_ADVOCAT) return "адвокат";
            if (role == Constants.STATUS_GIRL) return "девочка";
            if (role == Constants.STATUS_KOMISSAR_CHECKING) return "комиссар";
            if (role == Constants.STATUS_KOMISSAR_KILLING) return "стрелком";
            if (role == Constants.STATUS_KOMISSAR_REANIMATING) return "ренком";
            if (role == Constants.STATUS_MAFIA) return "мафиози";
            if (role == Constants.STATUS_MANIAC) return "маньяк";
            if (role == Constants.STATUS_REANIMATOR) return "реаниматор";
            if (role == Constants.STATUS_SHERIFF) return "шериф";
            if (role == Constants.STATUS_THIEF) return "вор";
            return "";
        }

        public static string GetUserStateString(GameUserData gdd)
        {
            if (gdd.UserState == Constants.USERSTATE_ALIVE)
            {
                return "жив" + GetGenderEnding(gdd.UserGender, true);
            }
            else if (gdd.UserState == Constants.USERSTATE_DEAD)
            {
                return "мертв" + GetGenderEnding(gdd.UserGender, true);
            }
            else if (gdd.UserState == Constants.USERSTATE_IMPRISONED)
            {
                return "посажен" + GetGenderEnding(gdd.UserGender, true);
            }
            else if (gdd.UserState == Constants.USERSTATE_IN_JAIL)
            {
                return "в тюрьме";
            }
            else if (gdd.UserState == Constants.USERSTATE_KILLED)
            {
                return "убит" + GetGenderEnding(gdd.UserGender, true);
            }
            else if (gdd.UserState == Constants.USERSTATE_AUTOKILLED)
            {
                if (gdd.UserGender == Constants.USERGENDER_THEY) return "спят вечным сном";
                return "спит вечным сном";
            }
            else if (gdd.UserState == Constants.USERSTATE_SELFKILLED)
            {
                return "сделал" + GetGenderEnding(gdd.UserGender, false)+" харакири";
            }
            return "ни рыба, ни мясо";
        }

        public static string GetUserStateStringByState(int state, int gender)
        {
            if (state == Constants.USERSTATE_ALIVE)
            {
                return "жив" + GetGenderEnding(gender, true);
            }
            else if (state == Constants.USERSTATE_DEAD)
            {
                return "мертв" + GetGenderEnding(gender, true);
            }
            else if (state == Constants.USERSTATE_IMPRISONED)
            {
                return "посажен" + GetGenderEnding(gender, true);
            }
            else if (state == Constants.USERSTATE_IN_JAIL)
            {
                return "в тюрьме";
            }
            else if (state == Constants.USERSTATE_KILLED)
            {
                return "убит" + GetGenderEnding(gender, true);
            }
            else if (state == Constants.USERSTATE_AUTOKILLED)
            {
                if (gender == Constants.USERGENDER_THEY) return "спят вечным сном";
                return "спит вечным сном";
            }
            else if (state == Constants.USERSTATE_SELFKILLED)
            {
                return "сделал" + GetGenderEnding(gender, false) + " харакири";
            }
            return "ни рыба, ни мясо";
        }

        public static int GetAliveMafia(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserState == Constants.USERSTATE_ALIVE && (r.UserRole == Constants.STATUS_MAFIA || r.UserRole == Constants.STATUS_THIEF || r.UserRole == Constants.STATUS_ADVOCAT)).Count();
        }

        public static int GetTotalMafia(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && (r.UserRole == Constants.STATUS_MAFIA || r.UserRole == Constants.STATUS_THIEF || r.UserRole == Constants.STATUS_ADVOCAT)).Count();
        }

        public static int GetAlivePlayers(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserState == Constants.USERSTATE_ALIVE).Count();
        }

        public static int GetTotalPlayers(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game).Count();
        }

        public static object GetDay(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.Games.Where(r => r.GameName == game).Select(t=>t.GameTurn).FirstOrDefault();
        }

        public static void SendEverybodyMail(string game, string subject, string body)
        {
            HeritageEntities ents = new HeritageEntities();
            foreach (GameUserData gud in ents.GameUserData.Where(r => r.GameName == game))
            {
                if (gud != null)
                {
                    SendMail(gud.UserEmail, subject, body);
                    if (gud.UserEmail2 != null && gud.UserID2 != null)
                    {
                        SendMail(gud.UserEmail2, subject, body);                        
                    }
                }
            }
        }

        public static void SendMafiaMail(string game, string subject, string body)
        {
            HeritageEntities ents = new HeritageEntities();
            foreach (GameUserData gud in ents.GameUserData.Where(r => r.GameName == game && (r.UserRole == Constants.STATUS_ADVOCAT || r.UserRole == Constants.STATUS_THIEF || r.UserRole == Constants.STATUS_MAFIA)))
            {
                if (gud != null)
                {
                    SendMail(gud.UserEmail, subject, body);
                    if (gud.UserEmail2 != null && gud.UserID2 != null)
                    {
                        SendMail(gud.UserEmail2, subject, body);
                    }
                }
            }
        }

        public static void StopGame(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games game = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            game.GameState = Constants.GAME_STATE_STOPPED;
            game.GameFinished = DateTime.Now;
            game.GameResult = Constants.GAMERESULT_DRAW;
            GameMessages gm = GameMessages.CreateGameMessages(1, id, DateTime.Now, "Вниманию всех участников!\n\nИгра была остановлена модератором или мэром.\n\nЭто автоматическое сообщение, отвечать на него не нужно.", false, Constants.ADRESAT_GAMEMASTER, Constants.ADRESAT_EVERYBODY, false, Guid.NewGuid(), game.GameTurn, 0);
            ents.GameMessages.AddObject(gm);
            ents.SaveChanges();
        }

        public static void RestartGame(string id)
        {
            HeritageEntities ents = new HeritageEntities();
            Games game = ents.Games.Where(r => r.GameName == id).FirstOrDefault();
            if (game.GameState == Constants.GAME_STATE_STOPPED)
            {
                game.GameFinished = null;
                game.GameResult = Constants.GAMERESULT_DRAW;
                game.GameTurn = 0;
                game.GameStarted = DateTime.Now;
                foreach (GameMessages gmm in ents.GameMessages.Where(r => r.Game == id))
                {
                    ents.GameMessages.DeleteObject(gmm);
                }
                GameMessages gm = GameMessages.CreateGameMessages(1, id, DateTime.Now, "Вниманию всех участников!\n\nИгра была РЕСТАРТОВАНА модератором или мэром.\nВсе игроки живы, статусы перераспределены по-новому, на дворе - 0 день.\n\nЭто автоматическое сообщение, отвечать на него не нужно", false, Constants.ADRESAT_GAMEMASTER, Constants.ADRESAT_EVERYBODY, false, Guid.NewGuid(), game.GameTurn, 0);
                ents.GameMessages.AddObject(gm);
                ents.SaveChanges();
                StartGame(id);
            }
        }

        public static string GetRibbons(Guid guid)
        {
            HeritageEntities ents = new HeritageEntities();
            string res = "";
            foreach (UserRibbons ur in ents.UserRibbons.Where(r => r.UserID == guid)) {
                if (ur.Ribbon == Constants.RIBBON_SPONSOR) {
                    res += "<img src=\"/Images/Icons/money_euro.png\" title=\"Спонсор проекта\"/>";
                }
            }
            return res;
        }

        public static bool IsMenuCollapsed()
        {
            Users u = GetCurrentUser();
            return u != null && u.MenuCollapsed;
        }

        const string HTML_TAG_PATTERN = "<.*?>";

        public static string GetStrippedText(string inputString)
        {
            inputString = inputString.Replace("<br>", "\n");
            inputString = inputString.Replace("</td>", "  ");
            inputString = inputString.Replace("</tr>", "\n");
            return Regex.Replace
              (inputString, HTML_TAG_PATTERN, string.Empty);
        }

        public static GameUserData GetMyGameUserDataInGame(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            Guid userID = GetCurrentUser().ID;
            GameUserData gud = ents.GameUserData.Where(r => (r.UserID == userID || r.UserID2 == userID) && r.GameName == game).FirstOrDefault();
            return gud;
        }

        public static bool IsMafia(GameUserData gud)
        {
            return gud != null && (gud.UserRole == Constants.STATUS_MAFIA || gud.UserRole == Constants.STATUS_THIEF || gud.UserRole == Constants.STATUS_ADVOCAT);
        }

        public static object GetMessageSubjectColor(GameMessages gm)
        {
            HeritageEntities ents = new HeritageEntities();
            Games game = ents.Games.Where(t => t.GameName == gm.Game).FirstOrDefault();
            GameUserData gme = GetMyGameUserDataInGame(game.GameName);
            GameUserData gud = ents.GameUserData.Where(t => t.GameName == gm.Game && t.UserID == gm.MessageTo).FirstOrDefault();
            GameUserData gudfrom = ents.GameUserData.Where(t => t.GameName == gm.Game && t.UserID == gm.MessageFrom).FirstOrDefault();
            string dateday = gm.MessageTime.ToString("dd.MM.yyyy HH:mm:ss")+" "+gm.MessageDay+"-й день, ";
            if (gudfrom != null && gme != null && gudfrom.UserID == gme.UserID)
            {
                if (gm.MessageTo == Constants.ADRESAT_EVERYBODY)                    
                {
                    if (gm.AuthorState == Constants.USERSTATE_ALIVE)
                    {
                        return "<span style=\"color:#039;\">" + dateday + " Вы сделали сообщение</span>";
                    }
                    else if (gm.AuthorState == Constants.USERSTATE_IMPRISONED || gm.AuthorState == Constants.USERSTATE_IN_JAIL)
                    {
                        return "<span style=\"color:#FF9900;\">" + dateday + " Заключенный по имени " + gudfrom.UserMask + " сделал сообщение</span>";
                    }
                    else if (gm.AuthorState == Constants.USERSTATE_KILLED || gm.AuthorState == Constants.USERSTATE_DEAD || gm.AuthorState == Constants.USERSTATE_SELFKILLED || gm.AuthorState == Constants.USERSTATE_AUTOKILLED)
                    {
                        return "<span style=\"color:magenta;\">" + dateday + " Загробный дух по имени " + gudfrom.UserMask + " сделал сообщение</span>";
                    }
                }
                if (gm.MessageTo == Constants.ADRESAT_MAFIA)
                {
                    return "<span style=\"color:olivedrab;\">" + dateday + " Вы написали мафии</span>";
                }
                if (gud != null)
                {
                    if (!gm.IsAnonymous)
                    {
                        return "<span style=\"color:#800;\">" + dateday + " Вы написали " + gud.UserMask + "</span>";
                    }
                    else
                    {
                        return "<span style=\"color:#800;\">" + dateday + " Вы написали анонимку " + gud.UserMask + "</span>";
                    }
                }
            }
            if (gud != null)
            {
                if (gm.MessageFrom == Constants.ADRESAT_GAZETA)
                {
                    return "<span style=\"color:#080;\">"+dateday+" Свежая газета для " + gud.UserMask+"</span>";
                }                    
                else if (gm.MessageFrom == Constants.ADRESAT_GAZETA_MORNING)
                {
                    return "<span style=\"color:#080;\">" + dateday + " Утренняя газета для " + gud.UserMask + "</span>";
                }
                else if (gm.MessageFrom == Constants.ADRESAT_GAMEMASTER)
                {
                    return "<span style=\"color:#f00;\">" + dateday + " Гейм-мастер написал Вам</span>";
                }
                else if (gm.IsAnonymous)
                {
                    return "<span style=\"color:#800\">" + dateday + " Получено подметное письмо</span>";
                }
                else
                {
                    return "<span style=\"color:#800;\">" + dateday + " " + gudfrom.UserMask + " написал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " Вам</span>";
                }
            }
            else if (gm.MessageTo == Constants.ADRESAT_EVERYBODY)
            {
                if (gudfrom != null)
                {
                    if (gm.AuthorState == Constants.USERSTATE_ALIVE)
                    {
                        return "<span style=\"color:#039;\">" + dateday + " " + gudfrom.UserMask + " сделал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " сообщение</span>";
                    }
                    else if (gm.AuthorState == Constants.USERSTATE_IMPRISONED || gm.AuthorState == Constants.USERSTATE_IN_JAIL)
                    {
                        return "<span style=\"color:#FF9900;\">" + dateday + " Заключенный по имени " + gudfrom.UserMask + " сделал сообщение</span>";
                    }
                    else if (gm.AuthorState == Constants.USERSTATE_KILLED || gm.AuthorState == Constants.USERSTATE_DEAD || gm.AuthorState == Constants.USERSTATE_SELFKILLED || gm.AuthorState == Constants.USERSTATE_AUTOKILLED)
                    {
                        return "<span style=\"color:magenta;\">" + dateday + " Загробный дух по имени " + gudfrom.UserMask + " сделал сообщение</span>";
                    }
                }
                else if (gm.MessageFrom == Constants.ADRESAT_GAMEMASTER)
                {
                    return "<span style=\"color:#f00;\">" + dateday + " Сообщение гейм-мастера</span>";
                }
                else if (gm.MessageFrom == Constants.ADRESAT_MAYOR)
                {
                    return "<span style=\"color:#080;\">" + dateday + " Сообщение мэра города</span>";
                }
                else if (gm.MessageFrom == Constants.ADRESAT_MODERATOR)
                {
                    return "<span style=\"color:#080;\">" + dateday + " Сообщение модератора</span>";
                }
            }
            else if (gm.MessageTo == Constants.ADRESAT_MAFIA) 
            {
                if (gudfrom != null)
                {
                    return "<span style=\"color:olivedrab;\">" + dateday + " " + gudfrom.UserMask + " написал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " мафии</span>";
                }
            }

            return "";
        }

        public static string GetGenderEnding(int gender, bool hard)
        {
            if (gender == Constants.USERGENDER_HE) return "";
            if (gender == Constants.USERGENDER_SHE) return "а";
            if (gender == Constants.USERGENDER_IT) return "о";
            if (gender == Constants.USERGENDER_THEY) return (hard? "ы":"и");
            return "";
        }

        public static string GetPossibleAdresseesDropDown(string game, GameMessages msg)
        {
            StringBuilder res = new StringBuilder();
            bool sel = false;
            res.Append("<select name=\"adressee\" style=\"min-width:170px;\">");
            string s = "";            
            if (msg != null && msg.MessageTo == Constants.ADRESAT_EVERYBODY)
            {
                s = " selected";
                sel = true;
            }
            res.Append(string.Format("<option value=\"{0}\""+s+">Всем</option>", Constants.ADRESAT_EVERYBODY));
            s = "";
            if (msg != null && msg.MessageTo == Constants.ADRESAT_MAFIA && !sel)
            {
                s = " selected";
                sel = true;
            }
            res.Append(string.Format("<option value=\"{0}\"" + s + ">MAFIA</option>", Constants.ADRESAT_MAFIA));
            s = "";
            foreach (GameUserData gud in Utils.GetAllGameUsersData(game))
            {
                if (msg != null && !sel)
                {
                    if ((msg.MessageFrom == Constants.ADRESAT_GAZETA || msg.MessageFrom == Constants.ADRESAT_GAZETA_MORNING) && msg.MessageTo == gud.UserID)
                    {
                        s = " selected";
                        sel = true;
                    }
                    if (msg.MessageFrom == gud.UserID && !msg.IsAnonymous)
                    {
                        s = " selected";
                        sel = true;
                    }
                }
                res.Append(string.Format("<option value=\"{0}\""+s+">{1}</option>", gud.UserID, gud.UserMask));
                s = "";
            }
            res.Append("</select> <span style=\"white-space:nowrap;\">Анонимно:&nbsp;<input type=\"checkbox\" name=\"anonymous\"/> (только для приватов)</span>");
            return res.ToString();
        }

        public static string GetQuotedText(string str)
        {
            str = str.Replace("<br>", "\n");
            str = "> "+str.Replace("\n", "\n> ");
            str = "\n\n\n" + str;
            return str;
        }

        public static string GetMailPrefix(GameUserData gud, Guid adr, bool anon)
        {
            if (anon) return "";
            if (adr == Constants.ADRESAT_MAFIA)
            {
                return gud.UserMask + " написал" + Utils.GetGenderEnding(gud.UserGender, false) + " мафии\n\n";
            }
            if (adr == Constants.ADRESAT_EVERYBODY)
            {
                if (gud.UserState == Constants.USERSTATE_ALIVE)
                {
                    return "Сообщение гражданина города "+gud.UserMask+"\n\n";
                }
                if (gud.UserState == Constants.USERSTATE_KILLED || gud.UserState == Constants.USERSTATE_DEAD || gud.UserState == Constants.USERSTATE_AUTOKILLED || gud.UserState == Constants.USERSTATE_SELFKILLED)
                {
                    return "Сообщение духа гражданина города " + gud.UserMask + "\n\n";
                }
                if (gud.UserState == Constants.USERSTATE_IMPRISONED || gud.UserState == Constants.USERSTATE_IN_JAIL)
                {
                    return "Сообщение из тюрьмы гражданина города " + gud.UserMask + "\n\n";
                }
            }
            return "#mail " + gud.UserMask + " " + gud.GameName + "\n\n";
        }

        public static string CreateNavigationPanel(string game, Guid prev, Guid next, Guid cur)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<div class=\"bigbutton\" style=\"display: table-cell\">");
            if (prev != Guid.Empty) 
            {
                sb.Append(string.Format("<a href=\"/Game/ShowMessage/?messageID={1}&game={0}\"><img src=\"/Images/Icons/book_previous.png\" /></a>", game, prev.ToString("N")));
            }
            else {
                sb.Append(string.Format("<img src=\"/Images/Icons/book_previous_disabled.png\" />"));
            }
            sb.Append("</div><div class=\"bigbutton\" style=\"display:table-cell\">"); 
            if (next != Guid.Empty) 
            {
                sb.Append(string.Format("<a href=\"/Game/ShowMessage/?messageID={1}&game={0}\"><img src=\"/Images/Icons/book_next.png\" /></a>", game, next.ToString("N")));
            }
            else {
                sb.Append(string.Format("<img src=\"/Images/Icons/book_next_disabled.png\" />"));
            }
            sb.Append("</div><div class=\"bigbutton\" style=\"display:table-cell; width:42px; margin-left:20px;text-align:right\">");
            if (MessageFlaggedByMe(cur, Constants.FLAGGED_AS_INTERESTING))
            {
                sb.Append("<a id=\"redflag\" href=\"#\" onclick=\"DeflagMessage()\"><img src=\"/Images/Icons/flag_red.png\" /></a>");
            }
            else
            {
                sb.Append("<a id=\"redflag\" href=\"#\" onclick=\"FlagMessage()\"><img src=\"/Images/Icons/flag_red_disabled.png\" /></a>");
            }
            sb.Append("</div>");
            return sb.ToString();
        }

        public static bool MessageFlaggedByMe(Guid cur, int p)
        {
            Guid userid = GetCurrentUser().ID;
            HeritageEntities ents = new HeritageEntities();
            if (ents.MessageFlags.Where(r => r.UserID == userid && r.MessageID == cur && r.FlagType == p).Count() > 0)
            {
                return true;
            }
            return false;
        }

        public static List<Guid> GetPrevAndNextMessages(string game, Guid guid, string filter)
        {
            List<Guid> kvp = new List<Guid>();
            GameUserData gud = Utils.GetMyGameUserDataInGame(game);
            if (string.IsNullOrEmpty(filter)) filter = "f:all";
            else if (filter == "f:mafia" && !Utils.IsMafia(gud))
            {
                filter = "f:all";
            }
            HeritageEntities ents = new HeritageEntities();
            Guid prev = Guid.Empty;
            Guid next = Guid.Empty;

            if (filter == "f:all")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && (r.MessageTo == Constants.ADRESAT_EVERYBODY || r.MessageTo == gudGuid || r.MessageFrom == gudGuid || (isMafia && r.MessageTo == Constants.ADRESAT_MAFIA))).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count-1) next = list[index + 1];
            }

            if (filter == "f:marked")
            {
                Guid myuid = GetCurrentUser().ID;
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.MessageFlags.Where(r => r.UserID == myuid).Join(ents.GameMessages.Where(r => r.Game == game && (r.MessageTo == Constants.ADRESAT_EVERYBODY || r.MessageTo == gudGuid || r.MessageFrom == gudGuid || (isMafia && r.MessageTo == Constants.ADRESAT_MAFIA))).OrderBy(t => t.MessageTime), u => u.MessageID, g => g.UID, (u, g) => g).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            if (filter == "f:mafia")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && r.MessageTo == Constants.ADRESAT_MAFIA && (r.MessageFrom == gudGuid || isMafia)).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            if (filter == "f:news")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && r.MessageTo == gudGuid && (r.MessageFrom == Constants.ADRESAT_GAZETA || r.MessageFrom == Constants.ADRESAT_GAZETA_MORNING)).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            if (filter == "f:private")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && r.IsAnonymous == false && r.MessageTo != Constants.ADRESAT_EVERYBODY && r.MessageTo != Constants.ADRESAT_MAFIA && (r.MessageFrom == gudGuid || (r.MessageTo == gudGuid && r.MessageFrom!=Constants.ADRESAT_GAZETA && r.MessageFrom!=Constants.ADRESAT_GAZETA_MORNING))).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            if (filter == "f:mayor")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && r.MessageFrom == Constants.ADRESAT_MAYOR).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            if (filter == "f:anonym")
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && r.IsAnonymous == true && r.MessageTo != Constants.ADRESAT_EVERYBODY && r.MessageTo != Constants.ADRESAT_MAFIA && (r.MessageFrom == gudGuid || r.MessageTo == gudGuid)).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            Guid filterGuid = Guid.Empty;

            if (Guid.TryParse(filter, out filterGuid))
            {
                bool isGuest = gud == null;
                Guid gudGuid = isGuest ? Guid.Empty : gud.UserID;
                bool isMafia = Utils.IsMafia(gud);
                List<Guid> list = ents.GameMessages.Where(r => r.Game == game && !r.IsAnonymous && r.MessageFrom == filterGuid && (r.MessageTo == Constants.ADRESAT_EVERYBODY || r.MessageTo == gudGuid || (isMafia && r.MessageTo == Constants.ADRESAT_MAFIA))).OrderBy(t => t.MessageTime).Select(t => t.UID).ToList();
                int index = list.IndexOf(guid);
                if (index > 0) prev = list[index - 1];
                if (index < list.Count - 1) next = list[index + 1];
            }

            kvp.Add(prev);
            kvp.Add(next);
            return kvp;
        }

        public static object GetColoredMessage(string str)
        {
            return GetColoredMessage(str, false);
        }


        public static object GetColoredMessage(string str, bool html)
        {
            string[] lines = str.Split(new[] { "\n" }, StringSplitOptions.None);
            if (html) lines = str.Split(new[] { "<br>" }, StringSplitOptions.None);
            StringBuilder sb = new StringBuilder();
            foreach (string s in lines)
            {
                string st = s.TrimStart();
                string color = "";
                int next = 0;
                if (st.StartsWith("snr>"))
                {
                    next = 4;
                    color = "#1e439a";
                }
                else if (st.StartsWith("s>"))
                {
                    next = 2;
                    color = "#1e439a";
                }
                else if (st.StartsWith(">"))
                {
                    next = 1;
                    color = "#1e439a";
                }
                if (color != "")
                {
                    st = st.Substring(next).TrimStart();
                    if (st.StartsWith("snr>"))
                    {
                        color = "#999";
                    }
                    else if (st.StartsWith("s>"))
                    {
                        color = "#999";
                    }
                    else if (st.StartsWith(">"))
                    {
                        color = "#999";
                    }
                }
                if (color != "")
                {
                    sb.Append(string.Format("<span style='color:{1}'>{0}</span><br>", s, color));
                }
                else
                {
                    sb.Append(string.Format("{0}<br>", s));
                }
            }
            return sb.ToString();
        }

        public static object GetVoteMask(Guid? guid, string game)
        {
            Guid g = (Guid)guid;
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserID == g).Select(t => t.UserMask).FirstOrDefault();
        }

        public static string GetNewspaper(Games g, GameUserData gud, bool morning, bool html)
        {
            return GetNewspaper(g, gud, morning, html, 0);
        }
        public static string GetNewspaper(Games g, GameUserData gud, bool morning, bool html, int vts)
        {
            string game = g.GameName;
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("<b>День {0}<br>", g.GameTurn));
            sb.Append(string.Format("Всего игроков {0}, мафии {1}. В живых игроков {2}, мафии {3}</b><br><br>", Utils.GetTotalPlayers(game), Utils.GetTotalMafia(game), Utils.GetAlivePlayers(game), Utils.GetAliveMafia(game)));
            bool game_active = true;
            if (g.GameState == Constants.GAME_STATE_STOPPED)
            {
                sb.Append("Игра остановлена.");
            }
            if (g.GameState == Constants.GAME_STATE_FINISHED)
            {
                game_active = false;
                sb.Append("Игра окончена! ");
                if (g.GameResult == Constants.GAMERESULT_CITIZENS_WON) sb.Append("Победили честные жители!");
                if (g.GameResult == Constants.GAMERESULT_MAFIA_WON) sb.Append("Победила мафия!");
                if (g.GameResult == Constants.GAMERESULT_MANIAC_WON) sb.Append("Победил маньяк!");
                if (g.GameResult == Constants.GAMERESULT_DRAW) sb.Append("Ничья.");
                sb.Append("<br><br>");
            }
            IList<GameUserData> gil = Utils.GetAllGameUsersData(game);
            sb.Append("<table class=\"news\">");
            foreach (GameUserData gd in gil)
            {
                sb.Append(string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>", gd.UserMask, Utils.GetUserStateString(gd), (game_active? Utils.GetUserRoleString(gud, gd) : Utils.GetUserRoleString(gd, gd)), (!morning && game_active && gd.UserState == Constants.USERSTATE_ALIVE ? "голосует за" : ""), (!morning && game_active && gd.UserState == Constants.USERSTATE_ALIVE ? Utils.GetVoteMask(gd.UserVote, game) : "")));
            }
            sb.Append("</table><br>");
            if (morning && g.GameTurn > 0)
            {
                sb.Append("<b>Голосование</b><br><br>");
                sb.Append("<table class=\"news\">");
                foreach (GameUserData gd in gil)
                {
                    if (gd.UserState == Constants.USERSTATE_ALIVE || gd.UserState == Constants.USERSTATE_IMPRISONED || gd.UserState == Constants.USERSTATE_KILLED || gd.OutDate == g.GameTurn-1)
                    {
                        sb.Append(string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>", gd.UserMask, "голосовал" + GetGenderEnding(gd.UserGender, false) + " за", GetUserMask((Guid)gd.UserVote, game)));
                    }
                }
                sb.Append("</table><br><br>");
                GameUserData jailed = GetJailedTonight(game);
                sb.Append(string.Format("Совет города ({0} голосами) отправил в тюрьму гражданина {1} - {2}<br>", GetJailedVotes(game, jailed), jailed.UserMask, GetUserRoleString(jailed, jailed)));
                foreach (GameUserData data in GetKilledTonight(game))
                {
                    sb.Append(string.Format("Этой ночью был{0} убит{1} {2} - {3}<br>", GetGenderEnding(data.UserGender, false), GetGenderEnding(data.UserGender, true), data.UserMask, GetUserRoleString(data, data)));
                }
                if (gud.UserRole == Constants.STATUS_KOMISSAR_CHECKING || gud.UserRole == Constants.STATUS_KOMISSAR_KILLING || gud.UserRole == Constants.STATUS_KOMISSAR_REANIMATING || gud.UserRole == Constants.STATUS_ADVOCAT)
                {
                    if (gud.UserSpecVote != gud.UserID && (gud.UserState == Constants.USERSTATE_ALIVE || gud.UserState == Constants.USERSTATE_KILLED))
                    {
                        GameUserData target = GetGameUserData(game, gud.UserSpecVote);
                        sb.Append(string.Format("Этой ночью Вы проверили {0} - {1}<br>", target.UserMask, GetUserRoleString(target, target)));
                    }
                }
            }
            else
            {
                IList<GameUserData> alldata = GetAllAliveGameUsersData(game);
                var votes = alldata.Select(r=>r.UserVote);
                var counts = votes.Distinct().Select(a=> new {A = a, B = votes.Count(q=>q == a)}).OrderBy(q => -q.B);
                if (counts.Count() > 0)
                {
                    int max = counts.First().B;
                    if (max > 1 || (alldata[0] != null && alldata[0].UserVote != alldata[0].UserID))
                    {
                        List<Guid?> data = counts.Where(a => a.B >= counts.First().B).Select(q => q.A).ToList();
                        sb.Append(string.Format("<b>Пока на посадку ({0} голосами):</b><br>", max));
                        foreach (Guid? d in data)
                        {
                            var ff = alldata.Where(r => r.UserID == d).FirstOrDefault();
                            if (ff != null)
                            {
                                sb.Append(string.Format("{0}<br>", ff.UserMask));
                            }
                        }
                        sb.Append("<br>");
                    }
                }
                if (game_active)
                {
                    if (gud.UserRole == Constants.STATUS_GIRL && gud.UserState == Constants.USERSTATE_ALIVE)
                    {
                        sb.Append("<b>Девочка:</b><br>");
                        if (gud.UserSpecVote == gud.UserID)
                        {
                            sb.Append("Вы отдыхаете<br>");
                        }
                        else
                        {
                            sb.Append(string.Format("Вы собираетесь посетить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                        }
                    }
                    if (gud.UserRole == Constants.STATUS_MANIAC && gud.UserState == Constants.USERSTATE_ALIVE)
                    {
                        sb.Append("<b>Маньяк:</b><br>");
                        if (gud.UserSpecVote == gud.UserID)
                        {
                            sb.Append("Вы отдыхаете<br>");
                        }
                        else
                        {
                            sb.Append(string.Format("Вы собираетесь убить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                        }
                    }
                    if (gud.UserRole == Constants.STATUS_SHERIFF && gud.UserState == Constants.USERSTATE_ALIVE)
                    {
                        sb.Append("<b>Шериф:</b><br>");
                        if (gud.UserSpecVote == gud.UserID)
                        {
                            sb.Append("Вы отдыхаете<br>");
                        }
                        else
                        {
                            sb.Append(string.Format("Вы собираетесь убить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                        }
                    }
                    if (gud.UserRole == Constants.STATUS_REANIMATOR && gud.UserState == Constants.USERSTATE_ALIVE)
                    {
                        sb.Append("<b>Реаниматор:</b><br>");
                        if (gud.UserSpecVote == gud.UserID)
                        {
                            sb.Append("Вы отдыхаете<br>");
                        }
                        else
                        {
                            sb.Append(string.Format("Вы собираетесь спасти гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                        }
                    }
                    if ((gud.UserRole == Constants.STATUS_KOMISSAR_CHECKING || gud.UserRole == Constants.STATUS_KOMISSAR_KILLING || gud.UserRole == Constants.STATUS_KOMISSAR_REANIMATING) && gud.UserState == Constants.USERSTATE_ALIVE)
                    {
                        sb.Append("<b>Комиссар:</b><br>");
                        if (gud.UserSpecVote == gud.UserID)
                        {
                            sb.Append("Вы отдыхаете<br>");
                        }
                        else
                        {
                            sb.Append(string.Format("Вы собираетесь проверить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                        }
                    }
                    if (gud.UserRole == Constants.STATUS_MAFIA || gud.UserRole == Constants.STATUS_ADVOCAT || gud.UserRole == Constants.STATUS_THIEF)
                    {
                        List<GameUserData> mafs = alldata.Where(r => r.UserRole == Constants.STATUS_MAFIA).ToList();
                        if (mafs.Count() > 0) sb.Append("<b>Мафия:</b><br>");
                        foreach (GameUserData maf in mafs)
                        {
                            if (maf.UserSpecVote == maf.UserID)
                            {
                                if (gud.UserID == maf.UserID) sb.Append("Вы отдыхаете<br>");
                                else if (maf.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{0} отдыхает<br>", maf.UserMask));
                                else sb.Append(string.Format("{0} отдыхают<br>", maf.UserMask));
                            }
                            else
                            {
                                if (gud.UserID == maf.UserID) sb.Append(string.Format("Вы собираетесь убить гражданина города {0}<br>", alldata.Where(r => r.UserID == maf.UserSpecVote).First().UserMask));
                                else if (maf.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{1} собирается убить гражданина города {0}<br>", alldata.Where(r => r.UserID == maf.UserSpecVote).First().UserMask, maf.UserMask));
                                else sb.Append(string.Format("{1} собираются убить гражданина города {0}<br>", alldata.Where(r => r.UserID == maf.UserSpecVote).First().UserMask, maf.UserMask));
                            }
                        }
                        GameUserData adv = alldata.Where(r => r.UserRole == Constants.STATUS_ADVOCAT).FirstOrDefault();
                        if (adv != null)
                        {
                            sb.Append("<b>Адвокат:</b><br>");
                            if (adv.UserSpecVote == adv.UserID)
                            {
                                if (gud.UserID == adv.UserID) sb.Append("Вы отдыхаете<br>");
                                else if (adv.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{0} отдыхает<br>", adv.UserMask));
                                else sb.Append(string.Format("{0} отдыхают<br>", adv.UserMask));
                            }
                            else
                            {
                                if (gud.UserID == adv.UserID) sb.Append(string.Format("Вы собираетесь проверить гражданина города {0}<br>", alldata.Where(r => r.UserID == adv.UserSpecVote).First().UserMask));
                                else if (adv.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{1} собирается проверить гражданина города {0}<br>", alldata.Where(r => r.UserID == adv.UserSpecVote).First().UserMask, adv.UserMask));
                                else sb.Append(string.Format("{1} собираются проверить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask, adv.UserMask));
                            }
                        }
                        GameUserData thief = alldata.Where(r => r.UserRole == Constants.STATUS_THIEF).FirstOrDefault();
                        if (thief != null)
                        {
                            sb.Append("<b>Вор:</b><br>");
                            if (thief.UserSpecVote == thief.UserID)
                            {
                                if (gud.UserID == thief.UserID) sb.Append("Вы отдыхаете<br>");
                                else if (thief.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{0} отдыхает<br>", thief.UserMask));
                                else sb.Append(string.Format("{0} отдыхают<br>", thief.UserMask));
                            }
                            else
                            {
                                if (gud.UserID == thief.UserID) sb.Append(string.Format("Вы собираетесь посетить гражданина города {0}<br>", alldata.Where(r => r.UserID == gud.UserSpecVote).First().UserMask));
                                else if (thief.UserGender != Constants.USERGENDER_THEY) sb.Append(string.Format("{1} собирается посетить гражданина города {0}<br>", alldata.Where(r => r.UserID == thief.UserSpecVote).First().UserMask, thief.UserMask));
                                else sb.Append(string.Format("{1} собираются посетить гражданина города {0}<br>", alldata.Where(r => r.UserID == thief.UserSpecVote).First().UserMask, thief.UserMask));
                            }
                        }
                    }
                }
            }
            if (!game_active)
            {
                if (g.DropMask)
                {
                    sb.Append("<br><b>Роли исполняли:</b><br><br><table>");
                    foreach (GameUserData gd in gil)
                    {
                        sb.Append(string.Format("<tr><td>{0}</td><td>-</td><td>{1}</td></tr>", gd.UserMask, (gd.UserID2 == null? GetUserByID(gd.UserID.ToString("N")).Username : GetUserByID(gd.UserID.ToString("N")).Username+" и "+GetUserByID(gd.UserID2.Value.ToString("N")).Username)));
                    }
                    sb.Append("</table><br><br>");
                }
                sb.Append(string.Format("<i>В течение трех дней после окончания партии, до {0}, вы можете оставить одну положительную и одну отрицательную оценку участникам партии.<br><br>Для этого воспользуйтесь пунктом \"Оценки\", если Вы играете через Веб, или почтовой командой #mark.</i><br>", g.GameFinished.Value.AddDays(3).ToString("HH:mm:ss dd.MM.yy")));
            }
            return sb.ToString();
        }

        public static GameUserData GetGameUserData(string game, Guid? guid)
        {
            if (guid == null) return null;
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && (r.UserID == guid || r.UserID2 == guid)).FirstOrDefault();
        }

        public static GameUserData GetJailedTonight(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserState == Constants.USERSTATE_IMPRISONED).FirstOrDefault();
        }
        public static List<GameUserData> GetKilledTonight(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserState == Constants.USERSTATE_KILLED).ToList();
        }
        public static int GetJailedVotes(string game, GameUserData jailed)
        {
            HeritageEntities ents = new HeritageEntities();
            return ents.GameUserData.Where(r => r.GameName == game && r.UserVote == jailed.UserID).Count();
        }

        private static GameUserData GetUserDataByRole(IList<GameUserData> data, int role, bool alive)
        {
            if (alive)
                return data.Where(r => r.UserState == Constants.USERSTATE_ALIVE && r.UserRole == role).FirstOrDefault();
            return data.Where(r => r.UserRole == role).FirstOrDefault();
        }

        private static List<GameUserData> GetAliveUserData(IList<GameUserData> data)
        {
            return data.Where(r => r.UserState == Constants.USERSTATE_ALIVE).ToList();
        }

        private static GameUserData GetUserDataByID(IList<GameUserData> data, Guid? id, bool alive)
        {
            if (alive)
                return data.Where(r => r.UserState == Constants.USERSTATE_ALIVE && r.UserID == id).FirstOrDefault();
            return data.Where(r => r.UserID == id).FirstOrDefault();
        }

        private static GameUserData GetAliveOrJustDeadUserDataByID(IList<GameUserData> data, int role)
        {
            return data.Where(r => (r.UserState == Constants.USERSTATE_ALIVE || r.UserState == Constants.USERSTATE_KILLED) && r.UserRole == role).FirstOrDefault();
        }

        private static List<GameUserData> GetAliveOrJustDeadMafia(IList<GameUserData> data)
        {
            return data.Where(r => (r.UserState == Constants.USERSTATE_ALIVE || r.UserState == Constants.USERSTATE_KILLED) && r.UserRole == Constants.STATUS_MAFIA).ToList();
        }

        private static List<GameUserData> GetAliveOrJustDeadManiacs(IList<GameUserData> data)
        {
            return data.Where(r => (r.UserState == Constants.USERSTATE_ALIVE || r.UserState == Constants.USERSTATE_KILLED) && r.UserRole == Constants.STATUS_MANIAC).ToList();
        }

        private static GameUserData MafiaGoal(IList<GameUserData> mafia, IList<GameUserData> all)
        {
            GameUserData goal = null;
            foreach (var maf in mafia)
            {
                if (maf.UserSpecVote == maf.UserID) continue;
                if (goal == null) goal = GetUserDataByID(all, maf.UserSpecVote, false);
                else if (goal.UserID != maf.UserSpecVote) return null;
            }
            if (goal == null || IsMafia(goal)) return null;
            if (goal.UserState != Constants.USERSTATE_ALIVE) return null;
            return goal;
        }

        public static void Turn(string game)
        {
            HeritageEntities ents = new HeritageEntities();
            Games g = ents.Games.Where(r => r.GameName == game).FirstOrDefault();
            if (g == null) return;
            if (g.GameState != Constants.GAME_STATE_IN_PROGRESS) return;
            IList<GameUserData> users = ents.GameUserData.Where(r => r.GameName == game).ToList();

            foreach (var user in users)
            {
                if (user.UserState == Constants.USERSTATE_KILLED) user.UserState = Constants.USERSTATE_DEAD;
                if (user.UserState == Constants.USERSTATE_IMPRISONED) user.UserState = Constants.USERSTATE_IN_JAIL;
            }

            //welcome to the jail, boy

            IList<GameUserData> alldata = users.Where(r => r.UserState == Constants.USERSTATE_ALIVE).ToList();
            var votes = alldata.Select(r=>r.UserVote);
            var counts = votes.Distinct().Select(a=> new {A = a, B = votes.Count(q=>q == a)}).OrderBy(q => -q.B);
            List<Guid?> data = counts.Where(a => a.B >= counts.First().B).Select(q => q.A).ToList();
            int num = new Random().Next(data.Count);

            GameUserData poorGuy = users.Where(r => r.UserID == data[num]).First();
            poorGuy.UserState = Constants.USERSTATE_IMPRISONED; //jailed!
            poorGuy.OutDate = g.GameTurn;

            //check for autokills & harakiri

            foreach (var user in users)
            {
                if (user.HarakiriMode)
                {
                    user.UserState = Constants.USERSTATE_SELFKILLED; //kiai-ya-haa! khaa... khe... ergh... true samurai, yep.
                }
                else if (user.UserState == Constants.USERSTATE_ALIVE)
                {
                    int msgs = ents.GameMessages.Where(r => r.Game == game && r.MessageDay == g.GameTurn && r.MessageFrom == user.UserID && r.MessageTo == Constants.ADRESAT_EVERYBODY).Count();
                    if (msgs == 0)
                    {
                        user.UserState = Constants.USERSTATE_AUTOKILLED; //in sleep he sang to me, in dreams he came... (C)
                        user.OutDate = g.GameTurn;
                    }
                }
            }

            //girl is fucking someone
            var girl = GetUserDataByRole(users, Constants.STATUS_GIRL, true);
            if (girl != null && girl.UserSpecVote != girl.UserID)
            {
                var fuckedPerson = GetUserDataByID(users, girl.UserSpecVote, true);
                if (fuckedPerson != null)
                {
                    fuckedPerson.UserSpecVote = fuckedPerson.UserID; //oh my, i've just fucked!
                }
            }

            //thief... watch your pockets!
            var thief = GetUserDataByRole(users, Constants.STATUS_THIEF, true);
            if (thief != null && thief.UserSpecVote != thief.UserID)
            {
                var fuckedPerson = GetUserDataByID(users, thief.UserSpecVote, true);
                if (fuckedPerson != null)
                {
                    fuckedPerson.UserSpecVote = fuckedPerson.UserID; //well, fucked too, but in unpleasant way, doh
                }
            }

            //how you can forget your fucking gun? you stupid dick! (C)

            var komissar = GetAliveOrJustDeadUserDataByID(users, Constants.STATUS_KOMISSAR_KILLING);
            if (komissar != null && komissar.UserSpecVote != komissar.UserID)
            {
                var fuckedPerson = GetUserDataByID(users, komissar.UserSpecVote, true);
                if (fuckedPerson != null && (IsMafia(fuckedPerson) || fuckedPerson.UserRole == Constants.STATUS_MANIAC))
                {
                    fuckedPerson.UserState = Constants.USERSTATE_KILLED; //pam-pam-param, pam-param-param-param
                    fuckedPerson.OutDate = g.GameTurn;
                }
            }

            //wild, wild west...

            var sheriff = GetAliveOrJustDeadUserDataByID(users, Constants.STATUS_SHERIFF);
            if (sheriff != null && sheriff.UserSpecVote != sheriff.UserID)
            {
                var fuckedPerson = GetUserDataByID(users, sheriff.UserSpecVote, true);
                if (fuckedPerson != null)
                {
                    fuckedPerson.UserState = Constants.USERSTATE_KILLED; //pam-pam-param, pam-param-param-param
                    fuckedPerson.OutDate = g.GameTurn;
                }
            }

            //do you think i'm sexual? sorry, i'm not.

            var maniacs = GetAliveOrJustDeadManiacs(users);
            foreach (var maniac in maniacs)
            {
                if (maniac != null && maniac.UserSpecVote != maniac.UserID)
                {
                    var fuckedPerson = GetUserDataByID(users, maniac.UserSpecVote, true);
                    if (fuckedPerson != null)
                    {
                        fuckedPerson.UserState = Constants.USERSTATE_KILLED; //pam-pam-param, pam-param-param-param
                        fuckedPerson.OutDate = g.GameTurn;
                    }
                }
            }
            var mafia = GetAliveOrJustDeadMafia(users);
            GameUserData mafiaGoal = MafiaGoal(mafia, users);
            if (mafia.Count > 0 && mafiaGoal != null)
            {
                mafiaGoal.UserState = Constants.USERSTATE_KILLED; //shot in the back by Buford Tannen, over a matter of eighty dollars (c)
                mafiaGoal.OutDate = g.GameTurn;
            }

            //time to heal. yes. i really know how to use defibrillator

            komissar = GetAliveOrJustDeadUserDataByID(users, Constants.STATUS_KOMISSAR_REANIMATING);
            if (komissar != null && komissar.UserSpecVote != komissar.UserID)
            {
                var healedPerson = GetUserDataByID(users, komissar.UserSpecVote, true);
                if (healedPerson != null && !IsMafia(healedPerson) && healedPerson.UserState == Constants.USERSTATE_KILLED)
                {
                    healedPerson.UserState = Constants.USERSTATE_ALIVE; //a miracle! i've rised from dead!!
                    healedPerson.OutDate =-1;
                }
            }

            var reanimator = GetAliveOrJustDeadUserDataByID(users, Constants.STATUS_REANIMATOR);
            if (reanimator != null && reanimator.UserSpecVote != reanimator.UserID)
            {
                var healedPerson = GetUserDataByID(users, reanimator.UserSpecVote, false);
                if (healedPerson != null && healedPerson.UserState == Constants.USERSTATE_KILLED)
                {
                    healedPerson.UserState = Constants.USERSTATE_ALIVE; //one miracle... two miracles... a consistence.
                    healedPerson.OutDate = -1;
                }
            }

            //yeah! good morning, lads!
            g.GameTurn = g.GameTurn + 1;

            List<GameUserData> alive = GetAliveUserData(users);
            if (alive.Count == 0) //draw
            {
                g.GameState = Constants.GAME_STATE_FINISHED;
                g.GameResult = Constants.GAMERESULT_DRAW;
                g.GameFinished = DateTime.Now;
                foreach (var user in users)
                {
                    user.UserResult = Constants.USERRESULT_DRAW;
                }
            }
            else if (alive.Count == 1 && alive[0].UserRole == Constants.STATUS_MANIAC)
            {
                g.GameState = Constants.GAME_STATE_FINISHED;                
                g.GameResult = Constants.GAMERESULT_MANIAC_WON;
                g.GameFinished = DateTime.Now;
                foreach (var user in users)
                {
                    if (user.UserID == alive[0].UserID) user.UserResult = Constants.USERRESULT_WON;
                    else user.UserResult = Constants.USERRESULT_LOST;
                }
            }
            else if (alive.Where(r => r.UserRole == Constants.STATUS_MAFIA || r.UserRole == Constants.STATUS_ADVOCAT || r.UserRole == Constants.STATUS_THIEF).Count() == alive.Count())
            {
                g.GameState = Constants.GAME_STATE_FINISHED;
                g.GameResult = Constants.GAMERESULT_MAFIA_WON;
                g.GameFinished = DateTime.Now;
                foreach (var user in users)
                {
                    if (IsMafia(user)) user.UserResult = Constants.USERRESULT_WON;
                    else user.UserResult = Constants.USERRESULT_LOST;
                }
            }
            else if (alive.Where(r => r.UserRole == Constants.STATUS_MAFIA || r.UserRole == Constants.STATUS_ADVOCAT || r.UserRole == Constants.STATUS_THIEF || r.UserRole == Constants.STATUS_MANIAC).Count() == 0)
            {
                g.GameState = Constants.GAME_STATE_FINISHED;
                g.GameResult = Constants.GAMERESULT_CITIZENS_WON;
                g.GameFinished = DateTime.Now;
                foreach (var user in users)
                {
                    if (IsMafia(user) || user.UserRole == Constants.STATUS_MANIAC) user.UserResult = Constants.USERRESULT_LOST;
                    else user.UserResult = Constants.USERRESULT_WON;
                }
            }
            if (g.GameState == Constants.GAME_STATE_IN_PROGRESS)
            {
                GameSchedule gs = GameSchedule.FromGameString(g.GameSchedule);
                gs.CalculateNextTurnTime(false);
                g.GameSchedule = gs.ToGameString();
            }
            ents.SaveChanges();

            GenerateMorningNewspaper(game);
            foreach (var user in users)
            {
                PersonalData u = ents.PersonalData.Where(r => r.UserID == user.UserID).First();
                if (user.UserState == Constants.USERSTATE_AUTOKILLED)
                {
                    user.UserResult = Constants.USERRESULT_LOST;
                    u.GameRating = u.GameRating - 1;
                }
                else if (user.UserState == Constants.USERSTATE_SELFKILLED)
                {
                    user.UserResult = Constants.USERRESULT_LOST;
                    u.GameRating = u.GameRating - 0.8;
                }
                else if (user.UserResult == Constants.USERRESULT_WON)
                {
                    if (user.UserRole == Constants.STATUS_MANIAC)
                        u.GameRating = u.GameRating + 2.4;
                    else u.GameRating = u.GameRating + 1.2; 
                    if (user.UserState == Constants.USERSTATE_ALIVE) u.GameRating = u.GameRating + 0.1;
                }
                else if (user.UserResult == Constants.USERRESULT_LOST)
                {
                    u.GameRating = u.GameRating + 0.8;
                }
                else if (user.UserResult == Constants.USERRESULT_DRAW)
                {
                    u.GameRating = u.GameRating + 1;
                }
                if (user.UserID2 != null)
                {
                    u = ents.PersonalData.Where(r => r.UserID == user.UserID2).First();
                    if (user.UserState == Constants.USERSTATE_AUTOKILLED)
                    {
                        u.GameRating = u.GameRating - 1;
                    }
                    else if (user.UserState == Constants.USERSTATE_SELFKILLED)
                    {
                        u.GameRating = u.GameRating - 0.8;
                    }
                    else if (user.UserResult == Constants.USERRESULT_WON)
                    {
                        if (user.UserRole == Constants.STATUS_MANIAC)
                            u.GameRating = u.GameRating + 2.4;
                        else u.GameRating = u.GameRating + 1.2;
                        if (user.UserState == Constants.USERSTATE_ALIVE) u.GameRating = u.GameRating + 0.1;
                    }
                    else if (user.UserResult == Constants.USERRESULT_LOST)
                    {
                        u.GameRating = u.GameRating + 0.8;
                    }
                    else if (user.UserResult == Constants.USERRESULT_DRAW)
                    {
                        u.GameRating = u.GameRating + 1;
                    }
                }
                user.UserVote = user.UserID;
                user.UserSpecVote = user.UserID;
            }
            ents.SaveChanges();
        }


        public static void HandleMessage(MailAddress mailAddress, string msgText)
        {
            HeritageEntities ents = new HeritageEntities();
            if (ents.GameUserData.Where(r => r.UserEmail.Trim() == mailAddress.Address.Trim() || r.UserEmail2.Trim() == mailAddress.Address.Trim()).Count() == 0)
            {
                LogService.Error("background", Constants.COMPONENT_BACKGROUND, "HandleOrder: There is no player in the game with address "+mailAddress.Address.Trim());                                
                return;
            }
            List<string> lines = msgText.Split(new[] {"\n"}, StringSplitOptions.None).ToList();
            string[] commands = lines[0].Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            string game = commands[1].Trim();
            string password = commands[2].Trim();
            try
            {
                if (msgText.StartsWith("#wall "))
                {
                    GameUserData who = ents.GameUserData.Where(r => (r.UserEmail.Trim() == mailAddress.Address.Trim() || r.UserEmail2.Trim() == mailAddress.Address.Trim()) && r.GameName == game).FirstOrDefault();
                    if (who == null)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nВы не можете писать в эту партию", lines[0]));
                        return;
                    }
                    if (who.GamePassword.Trim() != password && who.GamePassword2.Trim() != password)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nНеправильный пароль", lines[0]));
                        return;
                    }

                    GameMessages gm = GameMessages.CreateGameMessages(1, game, DateTime.Now, GetMailPrefix(who, Constants.ADRESAT_EVERYBODY, false) + msgText.Substring(lines[0].Length + 1), false, who.UserID, Constants.ADRESAT_EVERYBODY, false, Guid.NewGuid(), Utils.GetGame(game).GameTurn, who.UserState);
                    ents.GameMessages.AddObject(gm);
                    ents.SaveChanges();

                }
                if (msgText.StartsWith("#mafia "))
                {
                    GameUserData who = ents.GameUserData.Where(r => (r.UserEmail.Trim() == mailAddress.Address.Trim() || r.UserEmail2.Trim() == mailAddress.Address.Trim()) && r.GameName == game).FirstOrDefault();
                    if (who == null)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nВы не можете писать в эту партию", lines[0]));
                        return;
                    }
                    if (who.GamePassword.Trim() != password && who.GamePassword2.Trim() != password)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nНеправильный пароль", lines[0]));
                        return;
                    }

                    GameMessages gm = GameMessages.CreateGameMessages(1, game, DateTime.Now, GetMailPrefix(who, Constants.ADRESAT_MAFIA, false) + msgText.Substring(lines[0].Length + 1), false, who.UserID, Constants.ADRESAT_MAFIA, false, Guid.NewGuid(), Utils.GetGame(game).GameTurn, who.UserState);
                    ents.GameMessages.AddObject(gm);
                    ents.SaveChanges();

                }
                else if (msgText.StartsWith("#mail ") || msgText.StartsWith("#anonymous "))
                {
                    string adresat = commands[1].Trim();
                    game = commands[2].Trim();
                    password = commands[3].Trim();
                    GameUserData who = ents.GameUserData.Where(r => (r.UserEmail.Trim() == mailAddress.Address.Trim() || r.UserEmail2.Trim() == mailAddress.Address.Trim()) && r.GameName == game).FirstOrDefault();
                    GameUserData whom = ents.GameUserData.Where(r => r.UserMask == adresat && r.GameName == game).FirstOrDefault();
                    if (who == null)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nВы не можете писать в эту партию", lines[0]));
                        return;
                    }
                    if (whom == null)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nАдресат не найден", lines[0]));
                        return;
                    }
                    if (who.GamePassword.Trim() != password && who.GamePassword2.Trim() != password)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nНеправильный пароль", lines[0]));
                        return;
                    }

                    GameMessages gm = GameMessages.CreateGameMessages(1, game, DateTime.Now, GetMailPrefix(who, whom.UserID, msgText.StartsWith("#anonymous ")) + msgText.Substring(lines[0].Length + 1), false, who.UserID, whom.UserID, msgText.StartsWith("#anonymous "), Guid.NewGuid(), Utils.GetGame(game).GameTurn, who.UserState);
                    ents.GameMessages.AddObject(gm);
                    ents.SaveChanges();
                }
                else if (msgText.StartsWith("#order "))
                {
                    GameUserData who = ents.GameUserData.Where(r => (r.UserEmail.Trim() == mailAddress.Address.Trim() || r.UserEmail2.Trim() == mailAddress.Address.Trim()) && r.GameName == game).FirstOrDefault();
                    if (who == null)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nВы не можете отдавать приказы в этой партии", lines[0]));
                        return;
                    }
                    if (who.GamePassword.Trim() != password && who.GamePassword2.Trim() != password)
                    {
                        SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nНеправильный пароль", lines[0]));
                        return;
                    }

                    StringBuilder sb = new StringBuilder(string.Format("> {0}\n\n", lines[0]));
                    bool needZ = false;
                    bool needI = false;
                    foreach (string line in lines.Skip(1))
                    {
                        string trimmed = line.Trim();
                        if (trimmed.Length == 0) continue;
                        if (trimmed.StartsWith("V ")) {
                            string person = trimmed.Substring(2).Trim();
                            GameUserData vote = ents.GameUserData.Where(r => r.UserMask == person && r.GameName == game).FirstOrDefault();
                            if (vote == null) 
                            {
                                sb.Append(string.Format("> {0} - игрок не найден\n", trimmed));
                            }
                            else if (vote.UserState != Constants.USERSTATE_ALIVE) {
                                sb.Append(string.Format("> {0} - игрок уже мертв/посажен\n", trimmed));
                            }
                            else if (who.UserState != Constants.USERSTATE_ALIVE)
                            {
                                sb.Append(string.Format("> {0} - Вы уже мертвы/посажены и не можете голосовать\n", trimmed));
                            }
                            else 
                            {
                                sb.Append(string.Format("> {0} - OK\n", trimmed));
                                who.UserVote = vote.UserID;
                            }
                        }
                        else if (trimmed.StartsWith("S "))
                        {
                            string person = trimmed.Substring(2).Trim();
                            GameUserData vote = ents.GameUserData.Where(r => r.UserMask == person && r.GameName == game).FirstOrDefault();
                            if (vote == null)
                            {
                                sb.Append(string.Format("> {0} - игрок не найден\n", trimmed));
                            }
                            else if (vote.UserState != Constants.USERSTATE_ALIVE)
                            {
                                sb.Append(string.Format("> {0} - игрок уже мертв/посажен\n", trimmed));
                            }
                            else if (who.UserState != Constants.USERSTATE_ALIVE)
                            {
                                sb.Append(string.Format("> {0} - Вы уже мертвы/посажены и не можете делать спецвыбор\n", trimmed));
                            }
                            else if (who.UserRole == Constants.STATUS_CITIZEN)
                            {
                                sb.Append(string.Format("> {0} - откуда S-ка у чижа? :-))\n", trimmed));
                            }
                            else
                            {
                                sb.Append(string.Format("> {0} - OK\n", trimmed));
                                who.UserSpecVote = vote.UserID;
                            }
                        }
                        else if (trimmed == "Z")
                        {
                            sb.Append(string.Format("> {0} - OK\n", trimmed));
                            needZ = true;
                        }
                        else if (trimmed == "I")
                        {
                            Games gg = Utils.GetGame(game);
                            if (gg.GameState == Constants.GAME_STATE_IN_PROGRESS)
                            {
                                sb.Append(string.Format("> {0} - OK\n", trimmed));
                                needI = true;
                            }
                            else
                            {
                                sb.Append(string.Format("> {0} - игра еще не стартовала либо остановлена или закончена\n", trimmed));                                
                            }
                        }
                        else if (trimmed == "Q")
                        {
                            if (who.UserState != Constants.USERSTATE_ALIVE)
                            {
                                sb.Append(string.Format("> {0} - Вы уже посажены/убиты\n", trimmed));
                            }
                            else if (!who.HarakiriMode)
                                sb.Append(string.Format("> {0} - ночью вы сделаете харакири, повторное Q до хода отменит это\n", trimmed));
                            else
                                sb.Append(string.Format("> {0} - харакири отменено\n", trimmed));
                            who.HarakiriMode = !who.HarakiriMode;
                        }
                        else
                        {
                            sb.Append(string.Format("> {0} - не знаю этой команды\n", trimmed));
                        }
                    }
                    if (needI)
                    {
                        Games gg = Utils.GetGame(game);
                        sb.Append(string.Format("\n{0}\n", GameSchedule.FromGameString(gg.GameSchedule).NextTurnString()));
                    }
                    SendMail(mailAddress.Address, "Результат обработки приказа", sb.ToString());
                    ents.SaveChanges();
                    if (needZ)
                    {
                        Games g = GetGame(game);
                        string paper = GetStrippedText(GetNewspaper(g, who, false, false));
                        SendMail(mailAddress.Address, string.Format("[{0}] {1}-й день. Свежая газета для {2}", game, g.GameTurn, who.UserMask), paper);
                    }
                }
                else if (msgText.StartsWith("#mark "))
                {
                    SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nКоманда пока не поддерживается", lines[0]));
                    return;
                }
            }
            catch (Exception ex)
            {
                LogService.Error(Utils.GetPrincipalName(), Constants.COMPONENT_HANDLECOMMAND, string.Format("Order handling error: {0}\n\n{1}", ex, msgText));
                SendMail(mailAddress.Address, "Результат обработки команды", string.Format("> {0}\n\nНеправильная команда", lines[0]));
                return;
            }
        }

        public static string GetServerStars(string p)
        {
            double sr = 0;
            int offset = 0;
            double.TryParse(p, out sr);
            int yells = 0;
            if (sr < 1) yells = 1;
            int reds = (int)(sr / 1000);
            StringBuilder sb = new StringBuilder();
            sb.Append("<div style=\"float: left;display:table-cell;\">");
            for (int i = 0; i < reds; i++)
            {
                sb.Append("<img src=\"/images/icons/16/star_red.png\" style=\"position:relative;left:"+offset+"px;\">");
                offset -= 8;
            }
            sr = sr -reds * 1000;
            int blues = (int)(sr / 100);
            for (int i = 0; i < blues; i++)
            {
                sb.Append("<img src=\"/images/icons/16/star_blue.png\" style=\"position:relative;left:" + offset + "px;\">");
                offset -= 8;
            }
            sr = sr - blues * 100;
            int greens = (int)(sr / 10);
            for (int i = 0; i < greens; i++)
            {
                sb.Append("<img src=\"/images/icons/16/star_green.png\" style=\"position:relative;left:" + offset + "px;\">");
                offset -= 8;
            }
            sr = sr - greens * 10;
            int yellows = (int)sr;
            for (int i = 0; i < yellows+yells; i++)
            {
                sb.Append("<img src=\"/images/icons/16/star_yellow.png\" style=\"position:relative;left:" + offset + "px;\">");
                offset -= 8;
            }
            offset = offset + 10;
            sb.Append("</div><div style=\"float: left;position:relative;top:-4px; left:"+offset+"px\"><sup><b>(" + p + ")</b></sup></div>");
            return sb.ToString();
        }

        public static IList<Games> GetAllMyGames()
        {
            HeritageEntities ents = new HeritageEntities();
            Guid my = GetCurrentUser().ID;
            return ents.Games.Join(ents.GameUserData.Where(r => r.UserID == my || r.UserID2 == my), u => u.GameName, v => v.GameName, (u, v) => u).OrderBy(u=>u.GameState).ThenByDescending(u=>u.GameFinished).ThenByDescending(u=>u.GameInitiated).ToList();
        }

        public static string GetGameResultString(int res)
        {
            if (res == Constants.GAMERESULT_CITIZENS_WON) return "победили честные жители";
            if (res == Constants.GAMERESULT_MAFIA_WON) return "победила мафия";
            if (res == Constants.GAMERESULT_MANIAC_WON) return "победил маньяк";
            if (res == Constants.GAMERESULT_DRAW) return "ничья";
            return "";
        }

        public static string GetPrincipalName()
        {
            return Thread.CurrentPrincipal.Identity.Name;
        }

        public static void SocialMessageCreate(Guid whom, int msgType, string msgSubject, string msgBody)
        {
            HeritageEntities ents = new HeritageEntities();
            SocialMessages msg = SocialMessages.CreateSocialMessages(GetCurrentUser().ID, whom, DateTime.Now, msgType, Guid.NewGuid(), msgSubject, msgBody);
            ents.SocialMessages.AddObject(msg);
            ents.SaveChanges();
        }

        public static void SocialMessageCreateSystem(Guid whom, int msgType, string msgSubject, string msgBody)
        {
            HeritageEntities ents = new HeritageEntities();
            SocialMessages msg = SocialMessages.CreateSocialMessages(Constants.ADRESAT_ROBOT, whom, DateTime.Now, msgType, Guid.NewGuid(), msgSubject, msgBody);
            ents.SocialMessages.AddObject(msg);
            ents.SaveChanges();
        }

        public static string CreatePlayerOptionList(string guid)
        {
            return CreatePlayerOptionList(guid, false);
        }

        public static string CreatePlayerOptionList(string guid, bool anyway)
        {
            Guid g = GetCurrentUser().ID;
            Guid.TryParse(guid, out g);
            bool gm = IsGameMasterAndAbove() || anyway;
            HeritageEntities ents = new HeritageEntities();
            StringBuilder sb = new StringBuilder();
            foreach (var entity in ents.Users.Where(r=> (gm || r.UserRole!=Constants.USER_ROLE_TESTER) && r.Validated).OrderBy(r=>r.Username))
            {
                sb.Append(string.Format("<option value=\"{0}\"{1}>{2}</option>", entity.ID.ToString("N"), (g == entity.ID? " selected":""), entity.Username));
            }
            return sb.ToString();
        }

        public static void SaveImage(Bitmap bmpResized)
        {
            Users cur = GetCurrentUser();
            HeritageEntities ents = new HeritageEntities();
            PersonalData pd = ents.PersonalData.Where(r => r.UserID == cur.ID).First();
            MemoryStream res = new MemoryStream();
            bmpResized.Save(res, ImageFormat.Png);
            pd.Photo = res.ToArray();
            ents.SaveChanges();
        }

        public static string ToolIcons()
        {
            StringBuilder sb = new StringBuilder();
            int cnt = UnreadMailCount();
            if (cnt > 0)
            {
                sb.Append("<a href=\"/Social/Mail\"><img src=\"/Images/Icons/mail.png\" style=\"position:relative; top:0px;\"/></a><span style=\"color:#fff; position:absolute; top:11px; font-size:24px; font-weight:bold\">"+cnt+"</span>");
            }
            else
            {
                sb.Append("<a href=\"/Social/Mail\"><img src=\"/Images/Icons/mail_g.png\" style=\"position:relative; top:0px;\"/></a>");
            }
            //sb.Append("<img src=\"/Images/Icons/lightbulb_g.png\" style=\"position:relative; top:-2px;\"/>");                   
            //sb.Append("<img src=\"/Images/Icons/clock_g.png\" style=\"position:relative; top:-2px;\"/>");
            return sb.ToString();
        }

        private static int UnreadMailCount()
        {
            Guid myID = GetCurrentUser().ID;
            HeritageEntities ents = new HeritageEntities();
            IList<Guid> list = ents.SocialMessagesStatus.Where(r=>r.UserID == myID).Select(t=>t.MsgID).ToList();
            if (IsGameMasterAndAbove())
            {
                return ents.SocialMessages.Where(r => (r.MsgTo == Constants.ADRESAT_EVERYBODY || r.MsgTo == myID || r.MsgTo == Constants.ADRESAT_GAMEMASTER) && !list.Contains(r.MsgID) && (r.MsgType == Constants.SOCIALMESSAGE_TYPE_GAME_FEEDBACK || r.MsgType == Constants.SOCIALMESSAGE_TYPE_NEWS || r.MsgType == Constants.SOCIALMESSAGE_TYPE_EVENT || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PRIVATE || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC)).Count();
            }
            return ents.SocialMessages.Where(r => (r.MsgTo == Constants.ADRESAT_EVERYBODY || r.MsgTo == myID) && !list.Contains(r.MsgID) && (r.MsgType == Constants.SOCIALMESSAGE_TYPE_NEWS || r.MsgType == Constants.SOCIALMESSAGE_TYPE_EVENT || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PRIVATE || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE || r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC)).Count();
        }
        public static SocialMessagePack GetSocialMail(int page)
        {
            Guid myID = GetCurrentUser().ID;
            HeritageEntities ents = new HeritageEntities();
            IList<Guid> list = ents.SocialMessagesStatus.Where(r => r.UserID == myID && r.Status == Constants.SOCIALMESSAGE_STATUS_DELETED).Select(t => t.MsgID).ToList();
            List<SocialMessages> msgs;
            if (IsGameMasterAndAbove())
            {
                msgs =
                    ents.SocialMessages.Where(
                        r =>
                        (r.MsgTo == Constants.ADRESAT_EVERYBODY || r.MsgTo == myID ||
                         r.MsgTo == Constants.ADRESAT_GAMEMASTER) && !list.Contains(r.MsgID) &&
                        (r.MsgType == Constants.SOCIALMESSAGE_TYPE_GAME_FEEDBACK ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_NEWS ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_EVENT ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PRIVATE ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC)).OrderByDescending(
                             y => y.MsgDate).ToList();
            }
            else
            {
                msgs =
                    ents.SocialMessages.Where(
                        r =>
                        (r.MsgTo == Constants.ADRESAT_EVERYBODY || r.MsgTo == myID) && !list.Contains(r.MsgID) &&
                        (r.MsgType == Constants.SOCIALMESSAGE_TYPE_NEWS ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_EVENT ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PRIVATE ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE ||
                         r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC)).OrderByDescending(
                             y => y.MsgDate).ToList();
            }
            if (page < 1) page = 1;
            if ((page - 1) * Constants.PAGE_SIZE - 1 > msgs.Count()) page = (msgs.Count() - 1) / Constants.PAGE_SIZE + 1;
            List<SocialMessages> ll = msgs.Skip((page - 1) * Constants.PAGE_SIZE).Take(Constants.PAGE_SIZE).ToList();
            int allPages = (msgs.Count - 1)/Constants.PAGE_SIZE+1;
            return new SocialMessagePack() {AllPages = allPages, MessagesPage = ll};
        }

        public static bool IsRead(Guid guid)
        {
            Guid myID = GetCurrentUser().ID;
            HeritageEntities ents = new HeritageEntities();
            SocialMessagesStatus sms = ents.SocialMessagesStatus.Where(r => r.UserID == myID && r.MsgID == guid).FirstOrDefault();
            if (sms == null || sms.Status != Constants.SOCIALMESSAGE_STATUS_READ) return false;
            return true;
        }

        public static string GetSocialIcon(int messageType)
        {
            if (messageType == Constants.SOCIALMESSAGE_TYPE_EVENT) return "/images/glossy/rss-feed.png";
            if (messageType == Constants.SOCIALMESSAGE_TYPE_GAME_FEEDBACK) return "/images/glossy/chat.png";
            if (messageType == Constants.SOCIALMESSAGE_TYPE_NEWS) return "/images/glossy/information.png";
            if (messageType == Constants.SOCIALMESSAGE_TYPE_PRIVATE) return "/images/glossy/mail.png";
            if (messageType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE || messageType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC) return "/images/glossy/favourite.png";
            return "/images/glossy/ask.png";
        }

        public static void StartFullGames()
        {
            HeritageEntities ents = new HeritageEntities();
            if (!ents.ServerSettings.First().GameStartProcessing) return;
            foreach (Games gd in ents.Games.Where(r => r.GameState == Constants.GAME_STATE_AVAILABLE))
            {
                if (gd.Players <= ents.GameUserData.Where(r => r.GameName == gd.GameName).Count())
                {
                    GameSchedule gs = GameSchedule.FromGameString(gd.GameSchedule);
                    DateTime now = DateTime.Now;
                    bool hourstart = false;
                    if (now.Minute == 0 && gs.StartHour == now.Hour) hourstart = true;
                    if (gs.StartHour == -2 || hourstart)
                    {
                        LogService.Info(Utils.GetPrincipalName(), Constants.COMPONENT_BACKGROUND,
                                        string.Format(
                                            "Game {0} is full and needs to be started. Processing autostart...",
                                            gd.GameName));
                        Utils.StartGame(gd.GameName);
                    }
                }
            }
        }

        public static void PutGamesOnRecord()
        {
            HeritageEntities ents = new HeritageEntities();
            if (!ents.ServerSettings.First().GameStartProcessing) return;
            foreach (GameDescriptions gd in ents.GameDescriptions)
            {
                if (gd.StartCondition == Constants.START_CONDITION_ONE_IF_NONE)
                {
                    if (ents.Games.Where(r => r.GameType == gd.GameName && r.GameState != Constants.GAME_STATE_FINISHED).FirstOrDefault() == null)
                    {
                        AddGame(gd);
                    }
                }
                if (gd.StartCondition == Constants.START_CONDITION_ONE_ALWAYS)
                {
                    if (ents.Games.Where(r => r.GameType == gd.GameName && r.GameState == Constants.GAME_STATE_AVAILABLE).FirstOrDefault() == null)
                    {
                        AddGame(gd);
                    }
                }
            }
        }

        private static void AddGame(GameDescriptions gd)
        {
            HeritageEntities ents = new HeritageEntities();
            Random r = new Random();
            int players = r.Next(gd.MinPlayers, gd.MaxPlayers + 1);
            Games g = Games.CreateGames(gd.GameName.Trim() + gd.GameNumber, gd.GameName, DateTime.Now, 0, Constants.GAME_STATE_AVAILABLE, gd.RatingForEnter, gd.GameSchedule, gd.Mafia, gd.Komissar, gd.Reanimator, gd.Advocat, gd.Sheriff, gd.Maniac, gd.Girl, gd.Thief, players, gd.Private, gd.PublicVote, 0, true);
            GameDescriptions gdd = ents.GameDescriptions.Where(rf => rf.GameName == gd.GameName).FirstOrDefault();
            g.GameDescription = gd.GameDescription;
            ents.AddToGames(g);
            gdd.GameNumber = gdd.GameNumber + 1;
            ents.SaveChanges();
            LogService.Info(Utils.GetPrincipalName(), Constants.COMPONENT_BACKGROUND, string.Format("Game {0} needs to be added to record. Adding game.", g.GameName));
        }

        public static void ProcessMails()
        {
            HeritageEntities ents = new HeritageEntities();
            if (!ents.ServerSettings.First().EmailProcessing) return;
            POPClient client = new POPClient();
            try
            {
                client.Connect("localhost", 110, false);
                client.Authenticate("server@netmafia.ru", "BcKrgb11");
            }
            catch (Exception ex)
            {
                LogService.Error(Utils.GetPrincipalName(), Constants.COMPONENT_BACKGROUND, string.Format("POP3 Retrieve mails: cannot connect to server. {0} ", ex.Message));
            }
            int msgs = client.GetMessageCount();
            for (int i = 1; i <= msgs; i++)
            {
                try
                {
                    Message msg = client.GetMessage(i);
                    string msgText = msg.MessageBody[0].Trim();
                    Utils.HandleMessage(msg.Headers.From, msgText);
                    client.DeleteMessage(i);
                }
                catch (Exception ex)
                {
                    LogService.Error(Utils.GetPrincipalName(), Constants.COMPONENT_BACKGROUND, string.Format("POP3 Retrieve mails: cannot handle message. {0} ", ex.Message));
                }
            }
            client.Disconnect();
        }

        public static void SendMails()
        {
            HeritageEntities ents = new HeritageEntities();
            foreach (GameMessages gm in ents.GameMessages.Where(r => !r.IsProcessed))
            {
                Games game = ents.Games.Where(t => t.GameName == gm.Game).FirstOrDefault();
                GameUserData gud = ents.GameUserData.Where(t => t.GameName == gm.Game && t.UserID == gm.MessageTo).FirstOrDefault();
                GameUserData gudfrom = ents.GameUserData.Where(t => t.GameName == gm.Game && t.UserID == gm.MessageFrom).FirstOrDefault();

                string subject = string.Format("[{0}] День {1}. ", game.GameName, game.GameTurn);
                if (gud != null)
                {
                    if (gm.MessageFrom == Constants.ADRESAT_GAZETA)
                    {
                        subject += ("Свежая газета для " + gud.UserMask);
                    }
                    else if (gm.MessageFrom == Constants.ADRESAT_GAZETA_MORNING)
                    {
                        subject += ("Утренняя газета для " + gud.UserMask);
                    }
                    else if (gm.MessageFrom == Constants.ADRESAT_GAMEMASTER)
                    {
                        subject += ("Гейм-мастер написал Вам");
                    }
                    else if (gm.IsAnonymous)
                    {
                        subject += ("Получено подметное письмо");
                    }
                    else
                    {
                        subject += (gudfrom.UserMask + " написал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " Вам");
                    }
                    string body = gm.MessageText;
                    body = Utils.GetStrippedText(body);
                    Utils.SendMail(gud.UserEmail, subject, body);
                    if (gud.UserID2 != null && gud.UserEmail2 != null)
                    {
                        Utils.SendMail(gud.UserEmail2, subject, body);                        
                    }
                    gm.IsProcessed = true;
                    continue;
                }
                else if (gm.MessageTo == Constants.ADRESAT_EVERYBODY)
                {
                    if (gudfrom != null)
                    {
                        if (gudfrom.UserState == Constants.USERSTATE_ALIVE)
                        {
                            subject += (gudfrom.UserMask + " сделал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " сообщение");
                        }
                        else if (gudfrom.UserState == Constants.USERSTATE_IMPRISONED || gudfrom.UserState == Constants.USERSTATE_IN_JAIL)
                        {
                            subject += ("Заключенный по имени " + gudfrom.UserMask + " сделал сообщение");
                        }
                        else if (gudfrom.UserState == Constants.USERSTATE_KILLED || gudfrom.UserState == Constants.USERSTATE_DEAD || gudfrom.UserState == Constants.USERSTATE_SELFKILLED || gudfrom.UserState == Constants.USERSTATE_AUTOKILLED)
                        {
                            subject += ("Загробный дух по имени " + gudfrom.UserMask + " сделал сообщение");
                        }
                        string body = gm.MessageText;
                        body = Utils.GetStrippedText(body);
                        Utils.SendEverybodyMail(gm.Game, subject, body);
                        gm.IsProcessed = true;
                        continue;

                    }
                    else if (gm.MessageFrom == Constants.ADRESAT_GAMEMASTER)
                    {
                        subject += ("Сообщение гейм-мастера");
                    }
                    else if (gm.MessageFrom == Constants.ADRESAT_MAYOR)
                    {
                        subject += ("Сообщение мэра города");
                    }
                    else if (gm.MessageFrom == Constants.ADRESAT_MODERATOR)
                    {
                        subject += ("Сообщение модератора");
                    }
                    string bbody = gm.MessageText;
                    bbody = Utils.GetStrippedText(bbody);
                    Utils.SendEverybodyMail(gm.Game, subject, bbody);
                    gm.IsProcessed = true;
                    continue;
                }
                else if (gm.MessageTo == Constants.ADRESAT_MAFIA)
                {
                    if (gudfrom != null)
                    {
                        subject += (gudfrom.UserMask + " написал" + Utils.GetGenderEnding(gudfrom.UserGender, false) + " мафии");
                        string body = gm.MessageText;
                        body = Utils.GetStrippedText(body);
                        Utils.SendMafiaMail(gm.Game, subject, body);
                        gm.IsProcessed = true;
                        continue;
                    }
                }
            }
            ents.SaveChanges();
        }

        public static void MakeTurns()
        {
            HeritageEntities ents = new HeritageEntities();
            foreach (Games gm in ents.Games.Where(r => r.GameState == Constants.GAME_STATE_IN_PROGRESS))
            {
                if (IsTurningTimeNow(gm.GameSchedule))
                {
                    Turn(gm.GameName);
                }
            }
        }

        private static bool IsTurningTimeNow(string gameSchedule)
        {
            DateTime now = DateTime.Now;
            DateTime turn = GameSchedule.FromGameString(gameSchedule).NextTurn;
            if (now.Year == turn.Year && now.Month == turn.Month && now.Day == turn.Day && now.Hour == turn.Hour && now.Minute == turn.Minute)
                return true;
            return false;
        }

        public static bool InsideBannedInterval(DateTime cur)
        {
            HeritageEntities ents = new HeritageEntities();
            DateTime? start = ents.ServerSettings.First().PauseStart;
            DateTime? end = ents.ServerSettings.First().PauseEnd;
            if (start != null && end != null && cur.CompareTo(start.Value)>=0 && cur.CompareTo(end.Value) <0) return true;
            return false;
        }

        public static DateTime GetBannedIntervalEnd(DateTime cur)
        {
            HeritageEntities ents = new HeritageEntities();
            DateTime? end = ents.ServerSettings.First().PauseEnd;
            if (end != null) return end.Value;
            return DateTime.MinValue;
        }

        public static string HTML_GameStartTimeBox(GameSchedule schedule)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<select name=\"gamestarttime\">");
            sb.Append(string.Format("<option value=\"-2\"{0}>По заполнению</option>", schedule.StartHour==-2?" selected":""));
            sb.Append(string.Format("<option value=\"-1\"{0}>Вручную</option>", schedule.StartHour == -1 ? " selected" : ""));
            for (int i = 0; i < 24; i++)
            {
                sb.Append(string.Format("<option value=\"{1}\"{0}>{1}:00</option>", schedule.StartHour == i ? " selected" : "",i));
            }
            sb.Append("</select>");
            return sb.ToString();
        }

        public static string HoursAndMinutes(int firstDayInMinutes)
        {
            int hrs = firstDayInMinutes/60;
            int mins = firstDayInMinutes%60;
            string shrs = hrs < 10 ? "0" + hrs : "" + hrs;
            string smins = mins < 10 ? "0" + mins : "" + mins;
            return string.Format("{0}:{1}", shrs, smins);
        }

        public static string HTML_GameTimeIntervalBox(int interval, string name, bool end)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("<select name=\"{0}\">", name));
            for (int i = 0; i < 24; i++)
            {
                sb.Append(string.Format("<option value=\"{1}\"{0}>{1}:{2}</option>", interval == i ? " selected" : "", i, end?"59":"00"));
            }
            sb.Append("</select>");
            return sb.ToString();
        }

        public static string HTML_ChooseSocialMessageAdresseeBox(string name)
        {
            StringBuilder sb = new StringBuilder();
            Guid my = GetCurrentUser().ID;
            sb.Append(string.Format("<select name=\"{0}\">", name));
            bool admin = Utils.IsGameMasterAndAbove();
            if (admin)
            {
                sb.Append(string.Format("<option value=\"{0}\">{1}</option>", Constants.SOCIALMESSAGE_TYPE_NEWS, "Всем (новость)"));                
            }
            HeritageEntities ents = new HeritageEntities();
            foreach (var user in ents.Users.Where(r=>r.Validated))
            {
                if (user.ID != my)
                {
                    sb.Append(string.Format("<option value=\"{0}\">{1}</option>", user.ID.ToString("N"), user.Username));
                }
            }
            sb.Append("</select>");
            return sb.ToString();
        }

        public static List<Users> GetTodayCelebrations()
        {
            DateTime now = DateTime.Now;
            HeritageEntities ents = new HeritageEntities();
            try
            {
                return ents.Users.Join(
                    ents.PersonalData.Where(
                        r => r.Birthdate.Value.Day == now.Day && r.Birthdate.Value.Month == now.Month),
                    u => u.ID, v => v.UserID, (u, v) => u).ToList();
            }
            catch (Exception ex)
            {
                return new List<Users>();
            }
        }

        public static string GetUserYears(Guid id)
        {
            HeritageEntities ents = new HeritageEntities();
            var data = ents.PersonalData.Where(r => r.UserID == id).First();
            return (DateTime.Now.Year - data.Birthdate.Value.Year) + "";
        }

        public static SocialMessagePack GetFeedbacks(string userId, string s)
        {
            int page = 1;
            int.TryParse(s, out page);
            Guid g = Guid.Empty;
            Guid.TryParse(userId, out g);
            HeritageEntities ents = new HeritageEntities();
            List<SocialMessages> msgs = ents.SocialMessages.Where(
                r =>
                r.MsgTo == g &&
                (r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PRIVATE ||
                 r.MsgType == Constants.SOCIALMESSAGE_TYPE_PERSONAL_FEEDBACK_PUBLIC)).OrderByDescending(r=>r.MsgDate).ToList();
            SocialMessagePack pack = new SocialMessagePack();
            if (msgs.Count == 0)
            {
                pack.AllPages = 0;
                pack.MessagesPage = new List<SocialMessages>();
                return pack;
            }
            pack.AllPages = (msgs.Count - 1)/20 + 1;
            if (page < 1) page = 1;
            if (page > pack.AllPages) page = pack.AllPages;
            pack.MessagesPage = msgs.Skip((page - 1)*20).Take(20).ToList();
            return pack;
        }

        private static string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private static Random rand = new Random();

        private static string ConstructString()
        {
            string res = "";
            for (int i=0; i<10; i++)
            {
                res += chars[rand.Next(chars.Length)];
            }
            return res;
        }

        public static string CreateInvCode()
        {
            bool unique = false;
            string s = ConstructString();
            HeritageEntities ents = new HeritageEntities();
            while (!unique)
            {
                if (ents.RegistrationCodes.Where(r => r.RegisterCode == s).FirstOrDefault() != null) s = ConstructString();
                else unique = true;
            }
            return s;
        }
    }

    public struct GameUserPair
    {
        public Games Game { get; set; }
        public GameUserData User { get; set; }
    }
    public struct SocialMessagePack
    {
        public List<SocialMessages> MessagesPage { get; set; }
        public int AllPages { get; set; }
    }

}